
/*
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


#define TOP_LEVEL_WINDOW_XSIZE 500
#define TOP_LEVEL_WINDOW_YSIZE 430

/* pixels in padding for gtk_box_pack try values 0, 1, 5, 20 */
#define PACKPADDING 0

/* textborder */
#define DRAWING_DEFAULT_TEXTBORDER 4

#include "config.h"

#include <stdio.h>
#include <math.h>
#include <string.h>

#include <gtk/gtk.h>
#include <cairo.h>
#include <cairo-pdf.h>
#include <cairo-svg.h>

#include "main.h"
#include "misc.h"
#include "parse.h"
#include "misc.h"
#include "pos.h"
#include "nes.h"
#include "pos.h"
#include "uniqnode.h"
#include "color.h"
#include "options.h"
#include "uniqstring.h"

#include "libmooigraph.h"

char *inputfilename = (char *) 0;
FILE *inputfilestream = NULL;
int settings_init_folded = 0;

/* font for drawing */
const char *drawing_default_fontname = DRAWING_DEFAULT_FONTNAME;
int drawing_default_fontsize = DRAWING_DEFAULT_FONTSIZE;
int drawing_default_textborder = DRAWING_DEFAULT_TEXTBORDER;

gdouble zfactor = 1.0;
gdouble zzfactor = 1.0;

static int vxmin = 0;
static int vvxmin = 0;
static int vymin = 0;
static int vvymin = 0;

static int redo_position = 0;

/* top level window */
static GtkWidget *mainwindow1 = (GtkWidget *) 0;

/* zoom and (x,y) sliders */
static GtkWidget *vscale1;	/* zoom */
static GtkWidget *vscale2;	/* x offset */
static GtkWidget *hscale1;	/* y offset */
GtkObject *adjvscale1;
GtkObject *adjvscale2;
GtkObject *adjhscale1;

/* node info window double-click on mouse right button */
static GtkWidget *nodeinfo = NULL;

/* area where to draw */
static GtkWidget *drawingarea1 = NULL;

/* exposed size of draw area */
static int drawing_area_xsize = 0;
static int drawing_area_ysize = 0;

/* busy drawing */
static int indraw = 0;

/* forward declarations */
static void
textsizeun (struct unode *un, cairo_t * incrp, cairo_surface_t * insurface);
static void mainlayout (void);
static void top_level_window_main_quit (void);
static void on_top_level_window_quit1_activate (GtkMenuItem * menuitem,
						gpointer user_data);
static void on_top_level_window_fullscreen1_activate (GtkMenuItem * menuitem,
						      gpointer user_data);
static void on_top_level_window_unfullscreen1_activate (GtkMenuItem *
							menuitem,
							gpointer user_data);
static void
on_top_level_window_viewcenter1_activate (GtkMenuItem * menuitem,
					  gpointer user_data);
static void
on_top_level_window_viewwide1_activate (GtkMenuItem * menuitem,
					gpointer user_data);
static void
on_top_level_window_viewtreeview1_activate (GtkMenuItem * menuitem,
					    gpointer user_data);
static void on_top_level_window_open1_activate (GtkMenuItem * menuitem,
						gpointer user_data);
static void on_top_level_window_saveaspng1_activate (GtkMenuItem * menuitem,
						     gpointer user_data);
static void on_top_level_window_saveassvg1_activate (GtkMenuItem * menuitem,
						     gpointer user_data);
static void on_top_level_window_saveaspdf1_activate (GtkMenuItem * menuitem,
						     gpointer user_data);
static void on_top_level_window_saveastulip1_activate (GtkMenuItem * menuitem,
						       gpointer user_data);
static void on_top_level_window_saveastys1_activate (GtkMenuItem * menuitem,
						     gpointer user_data);
static void on_top_level_window_saveasdia1_activate (GtkMenuItem * menuitem,
						     gpointer user_data);
static void on_top_level_window_helpgraph1_activate (GtkMenuItem * menuitem,
						     gpointer user_data);
static void on_top_level_window_helpsubgraph1_activate (GtkMenuItem *
							menuitem,
							gpointer user_data);
static void on_top_level_window_helpnode1_activate (GtkMenuItem * menuitem,
						    gpointer user_data);
static void on_top_level_window_helpedge1_activate (GtkMenuItem * menuitem,
						    gpointer user_data);
static void on_top_level_window_helpsettings1_activate (GtkMenuItem *
							menuitem,
							gpointer user_data);
static void on_top_level_window_helpgui1_activate (GtkMenuItem * menuitem,
						   gpointer user_data);
static void on_top_level_window_helpoptions1_activate (GtkMenuItem * menuitem,
						       gpointer user_data);
static gboolean on_top_level_window_drawingarea1_expose_event (GtkWidget *
							       widget,
							       GdkEventExpose
							       * event,
							       gpointer
							       user_data);
static void prs (cairo_t * cr, struct unode *un, gint x0, gint y0,
		 char *message, int color, int edgelabel);
static gboolean on_mouse_clicked (GtkWidget * widget, GdkEventButton * event,
				  gpointer user_data);
static gboolean on_mouse_wheel (GtkWidget * widget, GdkEventScroll * event,
				gpointer user_data);
static void on_vscale1_changed (GtkAdjustment * adj);
static void on_vscale2_changed (GtkAdjustment * adj);
static void on_hscale1_changed (GtkAdjustment * adj);
static void textsizes (struct dln *nodelist);
static void button1_clicked (GtkWidget * widget, gpointer window);
static void button2_clicked (GtkWidget * widget, gpointer window);
static void button3_clicked (GtkWidget * widget, gpointer window);
static void check1_toggle (GtkWidget * widget, gpointer window);
static void check2_toggle (GtkWidget * widget, gpointer window);
static void check3_toggle (GtkWidget * widget, gpointer window);
static GtkWidget *window_helpgraph1 = NULL;
static GtkWidget *window_helpsubgraph1 = NULL;
static GtkWidget *window_helpnode1 = NULL;
static GtkWidget *window_helpedge1 = NULL;
static GtkWidget *window_helpsettings1 = NULL;
static GtkWidget *window_helpoptions1 = NULL;
static GtkWidget *window_helpgui1 = NULL;
static void do_nodeinfo (struct unode *un);
static void dotreeview (void);
static int drawing = 0;
static GtkWidget *treeview_window = NULL;
static GtkWidget *treeviewtext_view = NULL;

/* status line gtk buffers */
static GtkTextBuffer *entry1buffer;
static char charentry1buffer[64];

/* last dir used in file->open or save-as */
gchar *lastdir = NULL;

/* drawing mode 0 is centered, 1 is wide drawing */
int viewmode = 0;

/* start of program */
int
main (int argc, char *argv[])
{
  int status = 0;
  GtkWidget *vbox1;
  GtkWidget *hbox1;
  GtkWidget *hbox2;
  GtkWidget *menubar1;
  GtkWidget *menuitem1;
  GtkWidget *menuitem2;
  GtkWidget *menuitem3;
  GtkWidget *menuitem4;
  GtkWidget *menuitem1_menu;
  GtkWidget *menuitem2_menu;
  GtkWidget *menuitem3_menu;
  GtkWidget *menuitem4_menu;
  GtkWidget *open1;
  GtkWidget *saveaspng1;
  GtkWidget *saveassvg1;
  GtkWidget *saveaspdf1;
  GtkWidget *saveastulip1;
  GtkWidget *saveastys1;
  GtkWidget *saveasdia1;
  GtkWidget *quit1;
  GtkWidget *helpgraph1;
  GtkWidget *helpsubgraph1;
  GtkWidget *helpnode1;
  GtkWidget *helpedge1;
  GtkWidget *helpsettings1;
  GtkWidget *helpoptions1;
  GtkWidget *helpgui1;
  GtkWidget *fullscreen1;
  GtkWidget *unfullscreen1;
  GtkWidget *viewcenter1;
  GtkWidget *viewwide1;
  GtkWidget *viewtreeview1;
  GtkWidget *check1;
  GtkWidget *check2;
  GtkWidget *check3;
  GtkWidget *button1;
  GtkWidget *button2;
  GtkWidget *button3;
  GtkWidget *entry1;

  /* commandline options */
  options (argc, argv);

  /* layouter init */
  once_init ();

  /* font for drawing */
  drawing_default_fontname = DRAWING_DEFAULT_FONTNAME;
  drawing_default_fontsize = DRAWING_DEFAULT_FONTSIZE;
  drawing_default_textborder = DRAWING_DEFAULT_TEXTBORDER;

  /*
   *    gtk_init (&argc, &argv);
   *
   * calls the function gtk_init(gint *argc, gchar ***argv) which will be called in all GTK applications. 
   * This sets up a few things for us such as the default visual and color map and then proceeds to call 
   * gdk_init(gint *argc, gchar ***argv). This function initializes the library for use, sets up default 
   * signal handlers, and checks the arguments passed to your application on the command line, 
   * looking for one of the following:
   *
   *    * --gtk-module
   *    * --g-fatal-warnings
   *    * --gtk-debug
   *    * --gtk-no-debug
   *    * --gdk-debug
   *    * --gdk-no-debug
   *    * --display
   *    * --sync
   *    * --no-xshm
   *    * --name
   *    * --class
   *
   * It removes these from the argument list, leaving anything it does not recognize for your application 
   * to parse or ignore. This creates a set of standard arguments accepted by all GTK applications.
   *
   */

  /* do gtk init, gtk will grab the gtk specific options on command line */
  status = gtk_init_check (&argc, &argv);

  if (status == FALSE)
    {
      printf ("%s is in console mode and need in X11 graphical mode to run\n",
	      argv[0]);
      return (0);
    }

  /* top level outer window */
  mainwindow1 = gtk_window_new (GTK_WINDOW_TOPLEVEL);

  /* make sure to exit oke. */
  g_signal_connect ((gpointer) mainwindow1, "destroy",
		    G_CALLBACK (top_level_window_main_quit), NULL);

  /* needed for the cairo drawing */
  gtk_widget_set_app_paintable (mainwindow1, TRUE);

  /* use package string program name as set by configure in config.h */
  gtk_window_set_title (GTK_WINDOW (mainwindow1), PACKAGE_STRING);

  /* pre-set some size */
  gtk_window_set_default_size (GTK_WINDOW (mainwindow1),
			       TOP_LEVEL_WINDOW_XSIZE,
			       TOP_LEVEL_WINDOW_YSIZE);

  /* vbox1 is a menu bar */
  vbox1 = gtk_vbox_new (FALSE, 0);
  gtk_widget_show (vbox1);
  gtk_container_add (GTK_CONTAINER (mainwindow1), vbox1);

  /* menu bar in vbox1 */
  menubar1 = gtk_menu_bar_new ();
  gtk_widget_show (menubar1);
  gtk_box_pack_start ( /* box */ GTK_BOX (vbox1), /* child */ menubar1,
		      /* expand */ FALSE, /* fill */ FALSE,	/* padding */
		      PACKPADDING);

  /* menu items in menu bar in vbox1 */
  menuitem1 = gtk_menu_item_new_with_mnemonic ("File");
  gtk_container_add (GTK_CONTAINER (menubar1), menuitem1);
  gtk_widget_show (menuitem1);

  /* 'file' sub menu in menu items in menu bar in vbox1 */
  menuitem1_menu = gtk_menu_new ();
  gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem1), menuitem1_menu);

  /* 'open' in 'file' sub menu in menu items in menu bar in vbox1 */
  open1 = gtk_menu_item_new_with_mnemonic ("Open");
  gtk_container_add (GTK_CONTAINER (menuitem1_menu), open1);
  gtk_widget_show (open1);

  /* run this routine when selected 'open' in 'file' menu */
  g_signal_connect ((gpointer) open1, "activate",
		    G_CALLBACK (on_top_level_window_open1_activate), NULL);

  /* 'save as png' in 'file' sub menu in menu items in menu bar in vbox1 */
  saveaspng1 = gtk_menu_item_new_with_mnemonic ("Save as png");
  gtk_container_add (GTK_CONTAINER (menuitem1_menu), saveaspng1);
  gtk_widget_show (saveaspng1);

  /* run this routine when selected 'save as png' in 'file' menu */
  g_signal_connect ((gpointer) saveaspng1, "activate",
		    G_CALLBACK (on_top_level_window_saveaspng1_activate),
		    NULL);

  /* 'save as svg' in 'file' sub menu in menu items in menu bar in vbox1 */
  saveassvg1 = gtk_menu_item_new_with_mnemonic ("Save as svg");
  gtk_container_add (GTK_CONTAINER (menuitem1_menu), saveassvg1);
  gtk_widget_show (saveassvg1);

  /* run this routine when selected 'save as svg' in 'file' menu */
  g_signal_connect ((gpointer) saveassvg1, "activate",
		    G_CALLBACK (on_top_level_window_saveassvg1_activate),
		    NULL);

  /* 'save as pdf' in 'file' sub menu in menu items in menu bar in vbox1 */
  saveaspdf1 = gtk_menu_item_new_with_mnemonic ("Save as pdf");
  gtk_container_add (GTK_CONTAINER (menuitem1_menu), saveaspdf1);
  gtk_widget_show (saveaspdf1);

  /* run this routine when selected 'save as pdf' in 'file' menu */
  g_signal_connect ((gpointer) saveaspdf1, "activate",
		    G_CALLBACK (on_top_level_window_saveaspdf1_activate),
		    NULL);

  /* 'save as tulip' in 'file' sub menu in menu items in menu bar in vbox1 */
  saveastulip1 = gtk_menu_item_new_with_mnemonic ("Save as tlp");
  gtk_container_add (GTK_CONTAINER (menuitem1_menu), saveastulip1);
  gtk_widget_show (saveastulip1);

  /* run this routine when selected 'save as tulip' in 'file' menu */
  g_signal_connect ((gpointer) saveastulip1, "activate",
		    G_CALLBACK (on_top_level_window_saveastulip1_activate),
		    NULL);

  /* 'save as tys' in 'file' sub menu in menu items in menu bar in vbox1 */
  saveastys1 = gtk_menu_item_new_with_mnemonic ("Save as tys");
  gtk_container_add (GTK_CONTAINER (menuitem1_menu), saveastys1);
  gtk_widget_show (saveastys1);

  /* run this routine when selected 'save as tys' in 'file' menu */
  g_signal_connect ((gpointer) saveastys1, "activate",
		    G_CALLBACK (on_top_level_window_saveastys1_activate),
		    NULL);

  /* 'save as dia' in 'file' sub menu in menu items in menu bar in vbox1 */
  saveasdia1 = gtk_menu_item_new_with_mnemonic ("Save as dia");
  gtk_container_add (GTK_CONTAINER (menuitem1_menu), saveasdia1);
  gtk_widget_show (saveasdia1);

  /* run this routine when selected 'save as dia' in 'file' menu */
  g_signal_connect ((gpointer) saveasdia1, "activate",
		    G_CALLBACK (on_top_level_window_saveasdia1_activate),
		    NULL);

  /* optional to add here in 'file' sub menu in menu items in menu bar in vbox1 */

  /* 'quit' in 'file' sub menu in menu items in menu bar in vbox1 */
  quit1 = gtk_menu_item_new_with_mnemonic ("Quit");
  gtk_container_add (GTK_CONTAINER (menuitem1_menu), quit1);
  gtk_widget_show (quit1);

  /* run this routine when selected 'quit' in 'file' menu */
  g_signal_connect ((gpointer) quit1, "activate",
		    G_CALLBACK (on_top_level_window_quit1_activate), NULL);

  /* menu items in menu bar in vbox1 */
  menuitem4 = gtk_menu_item_new_with_mnemonic ("View");
  gtk_container_add (GTK_CONTAINER (menubar1), menuitem4);
  gtk_widget_show (menuitem4);

  /* 'view' sub menu in menu items in menu bar in vbox1 */
  menuitem4_menu = gtk_menu_new ();
  gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem4), menuitem4_menu);

  /* 'view->center' in 'view' sub menu in menu items in menu bar in vbox1 */
  viewcenter1 = gtk_menu_item_new_with_mnemonic ("Centered");
  gtk_container_add (GTK_CONTAINER (menuitem4_menu), viewcenter1);
  gtk_widget_show (viewcenter1);

  /* run this routine when selected 'view->center' in 'view' menu */
  g_signal_connect ((gpointer) viewcenter1, "activate",
		    G_CALLBACK (on_top_level_window_viewcenter1_activate),
		    NULL);

  /* 'view->wide' in 'view' sub menu in menu items in menu bar in vbox1 */
  viewwide1 = gtk_menu_item_new_with_mnemonic ("Wide");
  gtk_container_add (GTK_CONTAINER (menuitem4_menu), viewwide1);
  gtk_widget_show (viewwide1);

  /* run this routine when selected 'view->wide' in 'view' menu */
  g_signal_connect ((gpointer) viewwide1, "activate",
		    G_CALLBACK (on_top_level_window_viewwide1_activate),
		    NULL);

  /* 'view->treeview' in 'view' sub menu in menu items in menu bar in vbox1 */
  viewtreeview1 = gtk_menu_item_new_with_mnemonic ("Treeview");
  gtk_container_add (GTK_CONTAINER (menuitem4_menu), viewtreeview1);
  gtk_widget_show (viewtreeview1);

  /* run this routine when selected 'view->treeview' in 'view' menu */
  g_signal_connect ((gpointer) viewtreeview1, "activate",
		    G_CALLBACK (on_top_level_window_viewtreeview1_activate),
		    NULL);

  /* menu items in menu bar in vbox1 */
  menuitem3 = gtk_menu_item_new_with_mnemonic ("Fullscreen");
  gtk_container_add (GTK_CONTAINER (menubar1), menuitem3);
  gtk_widget_show (menuitem3);

  /* 'fullscreen' sub menu in menu items in menu bar in vbox1 */
  menuitem3_menu = gtk_menu_new ();
  gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem3), menuitem3_menu);

  /* 'fullscreen->fullscreen' in 'fullscreen' sub menu in menu items in menu bar in vbox1 */
  fullscreen1 = gtk_menu_item_new_with_mnemonic ("Fullscreen");
  gtk_container_add (GTK_CONTAINER (menuitem3_menu), fullscreen1);
  gtk_widget_show (fullscreen1);

  /* run this routine when selected 'fullscreen->fullscreen in 'fullscreen' menu */
  g_signal_connect ((gpointer) fullscreen1, "activate",
		    G_CALLBACK (on_top_level_window_fullscreen1_activate),
		    NULL);

  /* 'fullscreen->normal' in 'fullscreen' sub menu in menu items in menu bar in vbox1 */
  unfullscreen1 = gtk_menu_item_new_with_mnemonic ("Normal");
  gtk_container_add (GTK_CONTAINER (menuitem3_menu), unfullscreen1);
  gtk_widget_show (unfullscreen1);

  /* run this routine when selected 'fullscreen->normal' in 'fullscreen' menu */
  g_signal_connect ((gpointer) unfullscreen1, "activate",
		    G_CALLBACK (on_top_level_window_unfullscreen1_activate),
		    NULL);

  /* menu items in menu bar in vbox1 */
  menuitem2 = gtk_menu_item_new_with_mnemonic ("Help");
  gtk_container_add (GTK_CONTAINER (menubar1), menuitem2);
  gtk_widget_show (menuitem2);

  /* 'help' sub menu in menu items in menu bar in vbox1 */
  menuitem2_menu = gtk_menu_new ();
  gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem2), menuitem2_menu);

  /* 'help->graph' in 'help' sub menu in menu items in menu bar in vbox1 */
  helpgraph1 = gtk_menu_item_new_with_mnemonic ("Graph");
  gtk_container_add (GTK_CONTAINER (menuitem2_menu), helpgraph1);
  gtk_widget_show (helpgraph1);

  /* run this routine when selected 'help->graph' in 'help' menu */
  g_signal_connect ((gpointer) helpgraph1, "activate",
		    G_CALLBACK (on_top_level_window_helpgraph1_activate),
		    NULL);

  /* 'help->subgraph' in 'help' sub menu in menu items in menu bar in vbox1 */
  helpsubgraph1 = gtk_menu_item_new_with_mnemonic ("Subgraph");
  gtk_container_add (GTK_CONTAINER (menuitem2_menu), helpsubgraph1);
  gtk_widget_show (helpsubgraph1);

  /* run this routine when selected 'help->subgraph' in 'help' menu */
  g_signal_connect ((gpointer) helpsubgraph1, "activate",
		    G_CALLBACK (on_top_level_window_helpsubgraph1_activate),
		    NULL);

  /* 'help->node' in 'help' sub menu in menu items in menu bar in vbox1 */
  helpnode1 = gtk_menu_item_new_with_mnemonic ("Node");
  gtk_container_add (GTK_CONTAINER (menuitem2_menu), helpnode1);
  gtk_widget_show (helpnode1);

  /* run this routine when selected 'help->node' in 'help' menu */
  g_signal_connect ((gpointer) helpnode1, "activate",
		    G_CALLBACK (on_top_level_window_helpnode1_activate),
		    NULL);

  /* 'help->edge' in 'help' sub menu in menu items in menu bar in vbox1 */
  helpedge1 = gtk_menu_item_new_with_mnemonic ("Edge");
  gtk_container_add (GTK_CONTAINER (menuitem2_menu), helpedge1);
  gtk_widget_show (helpedge1);

  /* run this routine when selected 'help->edge' in 'help' menu */
  g_signal_connect ((gpointer) helpedge1, "activate",
		    G_CALLBACK (on_top_level_window_helpedge1_activate),
		    NULL);


  /* 'help->settings' in 'help' sub menu in menu items in menu bar in vbox1 */
  helpsettings1 = gtk_menu_item_new_with_mnemonic ("Settings");
  gtk_container_add (GTK_CONTAINER (menuitem2_menu), helpsettings1);
  gtk_widget_show (helpsettings1);

  /* run this routine when selected 'help->settings' in 'help' menu */
  g_signal_connect ((gpointer) helpsettings1, "activate",
		    G_CALLBACK (on_top_level_window_helpsettings1_activate),
		    NULL);

  /* 'help->options' in 'help' sub menu in menu items in menu bar in vbox1 */
  helpoptions1 = gtk_menu_item_new_with_mnemonic ("Options");
  gtk_container_add (GTK_CONTAINER (menuitem2_menu), helpoptions1);
  gtk_widget_show (helpoptions1);

  /* run this routine when selected 'help->options' in 'help' menu */
  g_signal_connect ((gpointer) helpoptions1, "activate",
		    G_CALLBACK (on_top_level_window_helpoptions1_activate),
		    NULL);

  /* 'help->gui' in 'help' sub menu in menu items in menu bar in vbox1 */
  helpgui1 = gtk_menu_item_new_with_mnemonic ("Gui");
  gtk_container_add (GTK_CONTAINER (menuitem2_menu), helpgui1);
  gtk_widget_show (helpgui1);

  /* run this routine when selected 'help->gui' in 'help' menu */
  g_signal_connect ((gpointer) helpgui1, "activate",
		    G_CALLBACK (on_top_level_window_helpgui1_activate), NULL);


  /* end of 'file' sub menu in menu items in menu bar in vbox1 */


  /* in hbox1
   * left zoom slider
   * drawing area
   * right y slider
   * below x slider
   */

  /* add next area to the vbox1 */
  hbox1 = gtk_hbox_new (FALSE, 0);
  gtk_box_pack_start ( /* box */ GTK_BOX (vbox1), /* child */ hbox1,
		      /* expand */ TRUE, /* fill */ TRUE,	/* padding */
		      PACKPADDING);
  gtk_widget_show (hbox1);

  /* vertical slider in hbox1 for the zoom factor 50% is 1:1 */
  adjvscale1 = gtk_adjustment_new (50, 0, 100, 0, 0, 0);
  zfactor = 1.0;
  vscale1 = gtk_vscale_new (GTK_ADJUSTMENT (adjvscale1));
  g_signal_connect (GTK_OBJECT (adjvscale1), "value_changed",
		    GTK_SIGNAL_FUNC (on_vscale1_changed), NULL);
  gtk_box_pack_start ( /* box */ GTK_BOX (hbox1), /* child */ vscale1,
		      /* expand */ FALSE, /* fill */ TRUE,	/* padding */
		      PACKPADDING);
  gtk_scale_set_draw_value (GTK_SCALE (vscale1), FALSE);
  gtk_widget_show (vscale1);

  /* where to draw in hbox1 */
  drawingarea1 = gtk_drawing_area_new ();
  gtk_box_pack_start ( /* box */ GTK_BOX (hbox1), /* child */ drawingarea1,
		      /* expand */ TRUE, /* fill */ TRUE,	/* padding */
		      PACKPADDING);
  gtk_widget_show (drawingarea1);
  g_signal_connect ((gpointer) drawingarea1, "expose_event",
		    G_CALLBACK
		    (on_top_level_window_drawingarea1_expose_event), NULL);

  /* mouse buttons */
  g_signal_connect ((gpointer) drawingarea1, "button-press-event",
		    G_CALLBACK (on_mouse_clicked), NULL);
  gtk_widget_set_events (drawingarea1, GDK_BUTTON_PRESS_MASK);

  /* mouse wheel */
  g_signal_connect ((gpointer) drawingarea1, "scroll-event",
		    G_CALLBACK (on_mouse_wheel), NULL);
  gtk_widget_add_events (drawingarea1, GDK_SCROLL_MASK);

  /* vertical slider in hbox1 for the y position range 0...100% of full image size */
  adjvscale2 = gtk_adjustment_new (0, 0, 100, 0, 0, 0);
  vscale2 = gtk_vscale_new (GTK_ADJUSTMENT (adjvscale2));
  g_signal_connect (GTK_OBJECT (adjvscale2), "value_changed",
		    GTK_SIGNAL_FUNC (on_vscale2_changed), NULL);
  gtk_box_pack_start ( /* box */ GTK_BOX (hbox1), /* child */ vscale2,
		      /* expand */ FALSE, /* fill */ TRUE,	/* padding */
		      PACKPADDING);
  gtk_scale_set_draw_value (GTK_SCALE (vscale2), FALSE);
  gtk_widget_show (vscale2);

  /* horizontal scroller 0..100% of drawing size */
  adjhscale1 = gtk_adjustment_new (0, 0, 100, 0, 0, 0);
  hscale1 = gtk_hscale_new (GTK_ADJUSTMENT (adjhscale1));
  g_signal_connect (GTK_OBJECT (adjhscale1), "value_changed",
		    GTK_SIGNAL_FUNC (on_hscale1_changed), NULL);
  gtk_box_pack_start ( /* box */ GTK_BOX (vbox1), /* child */ hscale1,
		      /* expand */ FALSE, /* fill */ TRUE,	/* padding */
		      PACKPADDING);
  gtk_scale_set_draw_value (GTK_SCALE (hscale1), FALSE);
  gtk_widget_show (hscale1);

  /* add next area to the vbox1 */
  hbox2 = gtk_hbox_new (FALSE, 0);

  gtk_box_pack_start ( /* box */ GTK_BOX (vbox1), /* child */ hbox2,
		      /* expand */ FALSE, /* fill */ FALSE,	/* padding */
		      PACKPADDING);
  gtk_widget_show (hbox2);

  /* draw spline edges or normal */
  check1 = gtk_check_button_new_with_label ("splines");
  gtk_box_pack_start ( /* box */ GTK_BOX (hbox2), /* child */ check1,
		      /* expand */ FALSE, /* fill */ FALSE,	/* padding */
		      PACKPADDING);
  if (option_splines)
    {
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check1), TRUE);
    }
  else
    {
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check1), FALSE);
    }
  g_signal_connect (check1, "clicked",
		    G_CALLBACK (check1_toggle), (gpointer) mainwindow1);
  gtk_widget_show (check1);

  check2 = gtk_check_button_new_with_label ("singlenodes");
  gtk_box_pack_start ( /* box */ GTK_BOX (hbox2), /* child */ check2,
		      /* expand */ FALSE, /* fill */ FALSE,	/* padding */
		      PACKPADDING);
  if (option_no_singlenodes)
    {
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check2), FALSE);
    }
  else
    {
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check2), TRUE);
    }
  g_signal_connect (check2, "clicked",
		    G_CALLBACK (check2_toggle), (gpointer) mainwindow1);
  gtk_widget_show (check2);

  check3 = gtk_check_button_new_with_label ("selfedges");
  gtk_box_pack_start ( /* box */ GTK_BOX (hbox2), /* child */ check3,
		      /* expand */ FALSE, /* fill */ FALSE,	/* padding */
		      PACKPADDING);
  if (option_selfedges)
    {
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check3), TRUE);
    }
  else
    {
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check3), FALSE);
    }
  g_signal_connect (check3, "clicked",
		    G_CALLBACK (check3_toggle), (gpointer) mainwindow1);
  gtk_widget_show (check3);

  button1 = gtk_button_new_with_label ("fold");
  gtk_box_pack_start ( /* box */ GTK_BOX (hbox2), /* child */ button1,
		      /* expand */ FALSE, /* fill */ FALSE,	/* padding */
		      PACKPADDING);
  g_signal_connect (G_OBJECT (button1), "clicked",
		    G_CALLBACK (button1_clicked), G_OBJECT (mainwindow1));
  gtk_widget_show (button1);

  button2 = gtk_button_new_with_label ("unfold");
  gtk_box_pack_start ( /* box */ GTK_BOX (hbox2), /* child */ button2,
		      /* expand */ FALSE, /* fill */ FALSE,	/* padding */
		      PACKPADDING);
  g_signal_connect (G_OBJECT (button2), "clicked",
		    G_CALLBACK (button2_clicked), G_OBJECT (mainwindow1));
  gtk_widget_show (button2);

  button3 = gtk_button_new_with_label ("fit");
  gtk_box_pack_start ( /* box */ GTK_BOX (hbox2), /* child */ button3,
		      /* expand */ FALSE, /* fill */ FALSE,	/* padding */
		      PACKPADDING);
  g_signal_connect (G_OBJECT (button3), "clicked",
		    G_CALLBACK (button3_clicked), G_OBJECT (mainwindow1));
  gtk_widget_show (button3);

  /*
   * entry1 = gtk_entry_new ();
   * gtk_entry_set_text (GTK_ENTRY(entry1),"use file->open to open a mooigraph or dot graph");
   */
  entry1 = gtk_text_view_new ();
  entry1buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (entry1));
  gtk_text_buffer_set_text (entry1buffer,
			    "use file->open to open a graph file", -1);
  gtk_text_view_set_editable (GTK_TEXT_VIEW (entry1), FALSE);
  gtk_box_pack_start ( /* box */ GTK_BOX (hbox2), /* child */ entry1,
		      /* expand */ FALSE, /* fill */ FALSE,	/* padding */
		      PACKPADDING);
  gtk_widget_show (entry1);

  /* put on screen */
  gtk_widget_show (mainwindow1);

  /* start with optional file from commandline */
  if (option_filename)
    {
      if (option_debug)
	{
	  printf ("%s(): starting with file %s from commandline\n",
		  __FUNCTION__, option_filename);
	}
      prepare4newdata ();
      inputfilename = option_filename;
      inputfilestream = fopen (inputfilename, "r");
      if (inputfilestream)
	{
	  /* 0 is unset, 2 is folded, 3 is unfolded subgraphs initially */
	  settings_init_folded = 0;
	  status = parse (inputfilename, inputfilestream);
	  if (status)
	    {
	    }
	  fclose (inputfilestream);
	  singlenodes ();
	  textsizes (nl);
	  mainlayout ();
	  drawing = 1;
	}
    }

  /* interact gui */
  gtk_main ();

  return (0);
}


/* finally stop the gui */
static void
top_level_window_main_quit (void)
{

  /* run the gtk internal routine to stop gtk_main() which is a for(){} loop */
  gtk_main_quit ();

  return;
}


/* 'quit' in 'file' menu activated - sub menu in menu items in menu bar in vbox1 */
static void
on_top_level_window_quit1_activate (GtkMenuItem * menuitem,
				    gpointer user_data)
{

  /* pacify gcc */
  if (menuitem)
    {
    }
  if (user_data)
    {
    }

  /* finally stop the gui */
  top_level_window_main_quit ();

  return;
}

static void
on_top_level_window_fullscreen1_activate (GtkMenuItem * menuitem,
					  gpointer user_data)
{
  if (menuitem)
    {
    }
  if (user_data)
    {
    }
  gtk_window_fullscreen (GTK_WINDOW (mainwindow1));
  return;
}

static void
on_top_level_window_unfullscreen1_activate (GtkMenuItem * menuitem,
					    gpointer user_data)
{
  if (menuitem)
    {
    }
  if (user_data)
    {
    }
  gtk_window_unfullscreen (GTK_WINDOW (mainwindow1));
  return;
}

/*  */
static void
on_top_level_window_viewcenter1_activate (GtkMenuItem * menuitem,
					  gpointer user_data)
{
  if (menuitem)
    {
    }
  if (user_data)
    {
    }
  if (drawing == 0)
    {
      return;
    }
  if (viewmode == 1)
    {
      viewmode = 0;
      /* recalc positions */
      position ();
/* redraw */
      gtk_widget_queue_draw (drawingarea1);
    }
  return;
}

static void
on_top_level_window_viewwide1_activate (GtkMenuItem * menuitem,
					gpointer user_data)
{
  if (menuitem)
    {
    }
  if (user_data)
    {
    }
  if (drawing == 0)
    {
      return;
    }
  if (viewmode == 0)
    {
      viewmode = 1;
      /* recalc positions */
      position ();
      /* redraw */
      gtk_widget_queue_draw (drawingarea1);
    }
  return;
}

static void
on_top_level_window_viewtreeview1_activate (GtkMenuItem * menuitem,
					    gpointer user_data)
{
  if (menuitem)
    {
    }
  if (user_data)
    {
    }
  if (drawing == 0)
    {
      return;
    }
  if (treeview_window == NULL)
    {
      dotreeview ();
    }
  return;
}

/* 'open' in 'file' menu activated - sub menu in menu items in menu bar in vbox1 */
static void
on_top_level_window_open1_activate (GtkMenuItem * menuitem,
				    gpointer user_data)
{
  GtkWidget *dialog = (GtkWidget *) 0;
  char *file_chooser_filename = (char *) 0;
  int status = 0;
  GtkFileChooser *chooser = NULL;

  if (menuitem)
    {
    }
  if (user_data)
    {
    }

  /* no valid data to draw */
  drawing = 0;

  /* wipe old tree view */
  if (treeview_window)
    {
      gtk_widget_destroy (treeview_window);
      treeview_window = NULL;
    }

  /* clean up old data */
  prepare4newdata ();

  /* see gimp source code howto */
  dialog = gtk_file_chooser_dialog_new ("Select Data File", 0,	/* parent_window */
					GTK_FILE_CHOOSER_ACTION_OPEN,
					GTK_STOCK_CANCEL,
					GTK_RESPONSE_CANCEL,
					GTK_STOCK_OPEN,
					GTK_RESPONSE_ACCEPT, NULL);
  chooser = GTK_FILE_CHOOSER (dialog);
  if (lastdir)
    {
      gtk_file_chooser_set_current_folder (chooser, lastdir);
    }

  /* run the window to select a input file */
  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
    {
      /* open button */
      file_chooser_filename =
	(char *) gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
      lastdir = gtk_file_chooser_get_current_folder (chooser);
    }
  else
    {
      /* cancel button */
    }

  /* */
  (void) gtk_widget_destroy (dialog);

  /* */
  if (file_chooser_filename)
    {

      inputfilename = strdup (file_chooser_filename);
      /* */
      (void) g_free (file_chooser_filename);

      file_chooser_filename = (char *) 0;
    }
  else
    {
      return;
    }

  /* font for drawing */
  drawing_default_fontname = DRAWING_DEFAULT_FONTNAME;
  drawing_default_fontsize = DRAWING_DEFAULT_FONTSIZE;

  inputfilestream = fopen (inputfilename, "r");
  if (inputfilestream == NULL)
    {
      return;
    }

  /* 0 is unset, 2 is folded, 3 is unfolded subgraphs initially */
  settings_init_folded = 0;
  status = parse (inputfilename, inputfilestream);
  fclose (inputfilestream);

  if (status)
    {
      /* parse error */
      drawing = 0;
      return;
    }

  singlenodes ();

  textsizes (nl);

  mainlayout ();

  /* set sliders to defaults */
  zfactor = 1.0;
  gtk_adjustment_set_value (GTK_ADJUSTMENT (adjvscale1), 50);
  vxmin = 0;
  vymin = 0;
  gtk_adjustment_set_value (GTK_ADJUSTMENT (adjvscale2), 0);
  gtk_adjustment_set_value (GTK_ADJUSTMENT (adjhscale1), 0);
  drawing = 1;

  /* re draw screen */
  gtk_widget_queue_draw (drawingarea1);

  return;
}

/* */
static void
mainlayout (void)
{
  struct dln *ptr = NULL;
  struct dle *ptre = NULL;
  struct lmgnodel *nptr = NULL;
  struct lmgedgel *eptr = NULL;
  struct dn *dptr = NULL;
  struct dn *lptr = NULL;
  struct de *e = NULL;
  struct de *ee = NULL;
  int i = 0;
  int j = 0;
  int nal = 0;

  /* fold subgraphs into summary nodes */
  fold ();
  /* update textsizes for summary nodes */
  textsizes (wnl);
  /* start graph library */
  lmg_init ();
  /* add graph library debug info */
  lmg_debug (option_lmgdebug);
  /* check nodes for in/out-going edges */
  hasinout ();

  /* first the nodes without edge connections */
  ptr = wnl;
  while (ptr)
    {
      if (ptr->un->bitflags.hasin == 0 && ptr->un->bitflags.hasout == 0)
	{
	  lmg_addnode (ptr->un->name);
	}
      ptr = ptr->next;
    }
  /* now the nodes without incoming edge connections and only outgoing edge connections */
  ptr = wnl;
  while (ptr)
    {
      if (ptr->un->bitflags.hasin == 0 && ptr->un->bitflags.hasout == 1)
	{
	  lmg_addnode (ptr->un->name);
	}
      ptr = ptr->next;
    }
  /* now the nodes with incoming edge connections and outgoing edge connections */
  ptr = wnl;
  while (ptr)
    {
      if (ptr->un->bitflags.hasin == 1 && ptr->un->bitflags.hasout == 1)
	{
	  lmg_addnode (ptr->un->name);
	}
      ptr = ptr->next;
    }
  /* now the nodes with only incoming edge connections and without outgoing edge connections */
  ptr = wnl;
  while (ptr)
    {
      if (ptr->un->bitflags.hasin == 1 && ptr->un->bitflags.hasout == 0)
	{
	  lmg_addnode (ptr->un->name);
	}
      ptr = ptr->next;
    }

  ptre = wel;
  while (ptre)
    {
      if (option_selfedges)
	{
	  /* allow selfedges */
	  lmg_addedge (ptre->ue->fn->name, ptre->ue->tn->name,
		       ptre->ue->label, ptre->ue->color, ptre->ue->style,
		       ptre->ue->textcolor, ptre->ue->fontsize,
		       ptre->ue->fontname, ptre->ue->bitflags.textbold,
		       ptre->ue->bitflags.textitalic,
		       ptre->ue->bitflags.textoblique);
	}
      else
	{
	  if (ptre->ue->bitflags.selfedge)
	    {
	      /* skip selfedge */
	    }
	  else
	    {
	      lmg_addedge (ptre->ue->fn->name, ptre->ue->tn->name,
			   ptre->ue->label, ptre->ue->color, ptre->ue->style,
			   ptre->ue->textcolor, ptre->ue->fontsize,
			   ptre->ue->fontname, ptre->ue->bitflags.textbold,
			   ptre->ue->bitflags.textitalic,
			   ptre->ue->bitflags.textoblique);
	    }
	}
      ptre = ptre->next;
    }
  lmg_main ();
  nlevels = lmg_nlevels ();
  levels = mgr_malloc (nlevels * sizeof (struct dn *));
  for (i = 0; i < nlevels; i++)
    {
      nal = lmg_nal (i);
      if (nal)
	{
	  dptr = mgr_malloc (sizeof (struct dn));
	  dptr->pos = 0;
	  dptr->level = i;
	  levels[i] = dptr;
	  if (nal > 1)
	    {
	      for (j = 1; j < nal; j++)
		{
		  lptr = mgr_malloc (sizeof (struct dn));
		  lptr->pos = j;
		  lptr->level = i;
		  dptr->next = lptr;
		  dptr = lptr;
		}
	    }
	}
    }
  for (i = 0; i < nlevels; i++)
    {
      nptr = lmg_nodehead (i);
      while (nptr)
	{
	  lptr =
	    getdn (nptr->node->tree_layout.level,
		   nptr->node->tree_layout.position);
	  if (nptr->node->name)
	    {
	      lptr->un = uniqnode (nptr->node->name);
	      if (lptr->un == NULL)
		{
		  /* add edgelabel node */
		  lptr->un = unode_new2 (nptr->node->name);
		  lptr->un->label = nptr->node->label;
		  lptr->un->textcolor = nptr->node->textcolor;
		  lptr->un->fontsize = nptr->node->fontsize;
		  lptr->un->fontname = nptr->node->fontname;
		  lptr->un->bitflags.edgelabel = 1;
		  lptr->un->bitflags.textbold = nptr->node->bitflags.textbold;
		  lptr->un->bitflags.textitalic =
		    nptr->node->bitflags.textitalic;
		  lptr->un->bitflags.textoblique =
		    nptr->node->bitflags.textoblique;
		  /* edgelabel has invisible shape box */
		  lptr->un->shape = NSHAPE_BOX;
		  textsizeun (lptr->un, NULL, NULL);
		}
	    }
	  else
	    {
	      lptr->un = NULL;
	    }
	  nptr = nptr->next;
	}
    }


  ee = del;
  del = NULL;
  while (ee)
    {
      e = ee->next;
      mgr_free (ee);
      ee = e;
    }

  eptr = lmg_edgehead ();
  while (eptr)
    {
      e = mgr_malloc (sizeof (struct de));
      e->from =
	getdn (eptr->edge->from->tree_layout.level,
	       eptr->edge->from->tree_layout.position);
      e->to =
	getdn (eptr->edge->to->tree_layout.level,
	       eptr->edge->to->tree_layout.position);
      e->color = eptr->edge->color;
      e->style = eptr->edge->style;
      ee = del;
      if (ee)
	{
	  while (ee)
	    {
	      if (ee->next == NULL)
		{
		  break;
		}
	      ee = ee->next;
	    }
	  ee->next = e;
	}
      else
	{
	  del = e;
	}
      eptr = eptr->next;
    }

  /* set absolute node position */
  position ();

  /* update status text line */
  memset (charentry1buffer, 0, 64);
  snprintf (charentry1buffer, 64 - 1, "%d nodes, %d edges, size (%d,%d)",
	    lmg_nrealnodes (), lmg_nrealedges (), maxx, maxy);
  gtk_text_buffer_set_text (entry1buffer, charentry1buffer, -1);

  return;
}


static void
draw_nodes (cairo_t * crp)
{
  int i;
  struct dn *dptr;

  /* all nodes in all levels */
  for (i = 0; i < nlevels; i++)
    {

      dptr = levels[i];
      while (dptr)
	{
	  if (dptr->un)
	    {
	      if (dptr->un->label)
		{
		  prs (crp, dptr->un, dptr->x0 - vvxmin, dptr->y0 - vvymin,
		       dptr->un->label, dptr->un->color,
		       dptr->un->bitflags.edgelabel);
		}
	    }
	  dptr = dptr->next;
	}
    }
  return;
}


static void
draw_edgespline (cairo_t * crp, int x0, int y0, int x1, int y1, int state)
{
  int test = 0;
  int hx = 0;
  int hy = 0;

  if (state)
    {
    }

  /* degree to radius is degree * (M_PI / 180) */
  test = 0;

  /* halfway */
  hx = (x1 - x0) / 2 + x0;
  hy = (y1 - y0) / 2 + y0;

  if (hx)
    {
    }
  if (hy)
    {
    }

  /* y0 is always smaller then y1 downwards drawing lines */
  cairo_move_to (crp, x0 - vvxmin, y0 - vvymin);

  if (test)
    {
      cairo_line_to (crp, x1 - vvxmin, y1 - vvymin);
      cairo_move_to (crp, x0 - vvxmin, y0 - vvymin);
    }

  if (x0 < x1)
    {
      /* line from left to right */
      cairo_curve_to (crp, (x0 + 1) - vvxmin, (y0 + 1) - vvymin, x0 - vvxmin,
		      (y0 + 10) - vvymin, hx - vvxmin, hy - vvymin);
      cairo_curve_to (crp, hx - vvxmin, hy - vvymin, x1 - vvxmin,
		      (y1 - 10) - vvymin, x1 - vvxmin, y1 - vvymin);
    }
  else
    {
      /* line from right to left */
      cairo_curve_to (crp, (x0 - 1) - vvxmin, (y0 - 1) - vvymin, x0 - vvxmin,
		      (y0 + 10) - vvymin, hx - vvxmin, hy - vvymin);
      cairo_curve_to (crp, hx - vvxmin, hy - vvymin, x1 - vvxmin,
		      (y1 - 10) - vvymin, x1 - vvxmin, y1 - vvymin);
    }

  cairo_move_to (crp, x1 - vvxmin, y1 - vvymin);
  return;
}

static void
draw_edges_from_arrow (cairo_t * crp, struct de *eptr)
{
}

/* draw arrow downwards at to node */
static void
draw_edges_to_arrow (cairo_t * crp, struct de *eptr)
{
  if (eptr->style == ESTYLE_INVIS) { return; }
  cairo_move_to (crp, eptr->to->x1 - vvxmin, eptr->to->y1 - vvymin);
  cairo_line_to (crp, eptr->to->x1 - vvxmin - 5, eptr->to->y1 - vvymin - 5);
  cairo_move_to (crp, eptr->to->x1 - vvxmin, eptr->to->y1 - vvymin);
  cairo_line_to (crp, eptr->to->x1 - vvxmin + 5, eptr->to->y1 - vvymin - 5);
  return;
}

static void
draw_edges_arrow (cairo_t * crp, struct de *eptr)
{
  if (eptr->reversed)
    {
      draw_edges_from_arrow (crp, eptr);
    }
  else
    {
      draw_edges_to_arrow (crp, eptr);
    }
  return;
}

static void
draw_edges (cairo_t * crp, int dosplines)
{
  static const double dashed[] = { 14.0, 6.0 };
  static int lendashed = sizeof (dashed) / sizeof (dashed[0]);
  static const double dotted[] = { 1.0 };
  static int lendotted = sizeof (dotted) / sizeof (dotted[0]);
  int lnr;
  int lng;
  int lnb;
  struct de *eptr = NULL;

  /* now the edges */
  eptr = del;
  while (eptr)
    {
      if (eptr->style == ESTYLE_INVIS)
	{
	  /* edge is in layout data but not visible in drawing */
	}
      else
	{
	  if (eptr->style == ESTYLE_DASHED)
	    {
	      cairo_set_dash (crp, dashed, lendashed, 1);
	    }
	  else if (eptr->style == ESTYLE_DOTTED)
	    {
	      cairo_set_dash (crp, dotted, lendotted, 0);
	    }
	  else
	    {
	      /* solid edge line */
	      cairo_set_dash (crp, dotted, 0, 0);
	    }
	  if (eptr->color)
	    {
	      lnr = (eptr->color & 0x00ff0000) >> 16;
	      lng = (eptr->color & 0x0000ff00) >> 8;
	      lnb = (eptr->color & 0x000000ff);
	      cairo_set_source_rgb (crp, lnr / 255.0, lng / 255.0,
				    lnb / 255.0);
	    }
	  else
	    {
	      /* black edge line */
	      cairo_set_source_rgb (crp, 0, 0, 0);
	    }
	  /* both nodes of the edge */
	  if (eptr->from && eptr->to)
	    {

	      if (dosplines)
		{
		  /* both real nodes */
		  if (eptr->from->un && eptr->to->un)
		    {
		      cairo_move_to (crp, eptr->from->x2 - vvxmin,
				     eptr->from->y2 - vvymin);
		      if (eptr->from->y2 != eptr->from->y3)
			{
			  cairo_line_to (crp, eptr->from->x2 - vvxmin,
					 eptr->from->y3 - vvymin);
			  draw_edgespline (crp, eptr->from->x2,
					   eptr->from->y3, eptr->to->x1,
					   eptr->to->y1, 0);
			}
		      else
			{
			  draw_edgespline (crp, eptr->from->x2,
					   eptr->from->y2, eptr->to->x1,
					   eptr->to->y1, 1);
			}
		      draw_edges_arrow (crp, eptr);
		    }
		  else
		    {
		      /* from real node to dummy node */
		      if (eptr->from->un && eptr->to->un == NULL)
			{
			  cairo_move_to (crp, eptr->from->x2 - vvxmin,
					 eptr->from->y2 - vvymin);
			  if (eptr->from->y2 != eptr->from->y3)
			    {
			      cairo_line_to (crp, eptr->from->x2 - vvxmin,
					     eptr->from->y3 - vvymin);
			      draw_edgespline (crp, eptr->from->x2,
					       eptr->from->y3, eptr->to->x1,
					       eptr->to->y1, 2);
			    }
			  else
			    {
			      draw_edgespline (crp, eptr->from->x2,
					       eptr->from->y2, eptr->to->x1,
					       eptr->to->y1, 3);
			    }
			}
		      else if (eptr->from->un == NULL && eptr->to->un)
			{
			  /* dummynode to real node */
			  cairo_move_to (crp,
					 eptr->from->x1 - vvxmin,
					 eptr->from->y1 - vvymin);
			  cairo_line_to (crp,
					 eptr->from->x2 - vvxmin,
					 eptr->from->y2 - vvymin);
			  draw_edgespline (crp, eptr->from->x2,
					   eptr->from->y2, eptr->to->x1,
					   eptr->to->y1, 4);
			  draw_edges_arrow (crp, eptr);
			}
		      else
			{
			  /* dummynode to dummynode */
			  cairo_move_to (crp, eptr->from->x1 - vvxmin,
					 eptr->from->y1 - vvymin);
			  cairo_line_to (crp,
					 eptr->from->x2 - vvxmin,
					 eptr->from->y2 - vvymin);
			  draw_edgespline (crp, eptr->from->x2,
					   eptr->from->y2, eptr->to->x1,
					   eptr->to->y1, 5);
			}
		    }
		}
	      else
		{
		  /* draw normal edge lines and not splines */
		  /* both real nodes */
		  if (eptr->from->un && eptr->to->un)
		    {
		      cairo_move_to (crp, eptr->from->x2 - vvxmin,
				     eptr->from->y2 - vvymin);
		      if (eptr->from->y2 != eptr->from->y3)
			{
			  cairo_line_to (crp, eptr->from->x2 - vvxmin,
					 eptr->from->y3 - vvymin);
			  cairo_line_to (crp, eptr->to->x1 - vvxmin,
					 eptr->to->y1 - vvymin);
			}
		      else
			{
			  cairo_line_to (crp, eptr->to->x1 - vvxmin,
					 eptr->to->y1 - vvymin);
			}
		      draw_edges_arrow (crp, eptr);
		    }
		  else
		    {
		      /* real node to dummy node */
		      if (eptr->from->un && eptr->to->un == NULL)
			{
			  cairo_move_to (crp, eptr->from->x2 - vvxmin,
					 eptr->from->y2 - vvymin);
			  if (eptr->from->y2 != eptr->from->y3)
			    {
			      cairo_line_to (crp, eptr->from->x2 - vvxmin,
					     eptr->from->y3 - vvymin);
			      cairo_line_to (crp, eptr->to->x1 - vvxmin,
					     eptr->to->y1 - vvymin);
			    }
			  else
			    {
			      cairo_line_to (crp, eptr->to->x1 - vvxmin,
					     eptr->to->y1 - vvymin);
			    }
			}
		      else if (eptr->from->un == NULL && eptr->to->un)
			{
			  /* dummynode to real node */
			  cairo_move_to (crp, eptr->from->x1 - vvxmin,
					 eptr->from->y1 - vvymin);
			  cairo_line_to (crp, eptr->from->x2 - vvxmin,
					 eptr->from->y2 - vvymin);
			  cairo_line_to (crp, eptr->to->x1 - vvxmin,
					 eptr->to->y1 - vvymin);
			  draw_edges_arrow (crp, eptr);
			}
		      else
			{
			  /* both dummynode */
			  cairo_move_to (crp, eptr->from->x1 - vvxmin,
					 eptr->from->y1 - vvymin);
			  cairo_line_to (crp, eptr->from->x2 - vvxmin,
					 eptr->from->y2 - vvymin);
			  cairo_line_to (crp, eptr->to->x1 - vvxmin,
					 eptr->to->y1 - vvymin);
			}
		    }
		}
	      cairo_stroke (crp);
	    }
	}
      eptr = eptr->next;
    }
  return;
}

/* drawing area */
static gboolean
on_top_level_window_drawingarea1_expose_event (GtkWidget * widget,
					       GdkEventExpose * event,
					       gpointer user_data)
{
  cairo_t *crdraw = NULL;
  PangoLayout *layout;
  gint w;
  gint h;
  int dosplines = 0;
  const char *leadin =
    "/*\n"
    " *  Mooigraph is a GNU GPL free software program to draw graphs.\n"
    " *\n"
    " *  This program is free software: you can redistribute it and/or modify\n"
    " *  it under the terms of the GNU General Public License as published by\n"
    " *  the Free Software Foundation, either version 3 of the License, or\n"
    " *  (at your option) any later version.\n"
    " *\n"
    " *  This program is distributed in the hope that it will be useful,\n"
    " *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
    " *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
    " *  GNU General Public License for more details.\n"
    " *\n"
    " *  You should have received a copy of the GNU General Public License\n"
    " *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n"
    " *\n"
    " * Free software is a matter of liberty, not price.\n"
    " * To understand the concept, you should think of “free” as in “free speech”, not as in “free beer”.\n"
    " * \n"
    " * Free software means users of a program have the four essential freedoms:\n"
    " * \n"
    " * The freedom to run the program as you wish, for any purpose (freedom 0).\n"
    " * The freedom to study how the program works, and adapt it to your needs (freedom 1).\n"
    " * Access to the source code is a precondition for this.\n"
    " * The freedom to redistribute copies so you can help your neighbor (freedom 2).\n"
    " * The freedom to improve the program, and release your improvements to the public,\n"
    " * so that the whole community benefits (freedom 3).\n"
    " * Access to the source code is a precondition for this.\n"
    " *\n" " *  https://code.google.com/p/mooigraph/\n" " */ \n";

  if (widget)
    {
    }
  if (user_data)
    {
    }

  indraw = 1;
  dosplines = option_splines;
  vvxmin = vxmin;
  vvymin = vymin;
  zzfactor = zfactor;

  /* how large drawing area is */
  (void) gdk_drawable_get_size (event->window, &w, &h);

  /* save a copy of current size */
  drawing_area_xsize = w;
  drawing_area_ysize = h;

  /* */
  crdraw = gdk_cairo_create (event->window);

  /* no drawing data then draw splash screen */
  if (drawing == 0)
    {
      /* white fill drawing area */
      cairo_set_source_rgb (crdraw, 1, 1, 1);
      cairo_rectangle (crdraw, 0, 0, w, h);
      cairo_fill (crdraw);
      cairo_scale (crdraw, 0.5, 0.5);
      cairo_set_source_rgb (crdraw, 0, 0, 0);
      cairo_move_to (crdraw, 10, 10);
      cairo_show_text (crdraw, PACKAGE_STRING);
      cairo_move_to (crdraw, 10, 10);
      layout = pango_cairo_create_layout (crdraw);
      pango_layout_set_text (layout, leadin, -1);
      pango_cairo_show_layout (crdraw, layout);
      cairo_stroke (crdraw);
      g_object_unref (G_OBJECT (layout));
      cairo_destroy (crdraw);
      indraw = 0;
      return FALSE;
    }

  if (redo_position)
    {
      position ();
    }

  /* fill drawing background with background color */
  cairo_set_source_rgb (crdraw, bgcr / 255.0, bgcg / 255.0, bgcb / 255.0);
  cairo_rectangle (crdraw, 0, 0, w, h);
  cairo_fill (crdraw);

  /* use zoom slider drawing scale */
  cairo_scale (crdraw, zzfactor, zzfactor);

  draw_nodes (crdraw);
  draw_edges (crdraw, dosplines);

  cairo_destroy (crdraw);
  indraw = 0;

  return FALSE;
}


static void
prs (cairo_t * crp, struct unode *un, gint x0, gint y0, char *message,
     int color, int edgelabel)
{
  cairo_font_weight_t textweight;
  cairo_font_slant_t textslant;
  cairo_text_extents_t extents;
  int r = 0;
  int g = 0;
  int b = 0;
  int tr = 0;
  int tg = 0;
  int tb = 0;
  int unty = 0;
  char buf[1024];
  int ibuf = 0;
  char *cptr = NULL;
  gint dpwi = 0;
  gint dphi = 0;
  int textxsize = 0;
  int textysize = 0;
  int i = 0;
  int xo;
  int yo;
  double rbaspect;		/* aspect ratio */
  double rbcorner_radius;	/* and corner curvature radius */
  double rbradius;
  double rbdegrees;

  /* jitter */
  redo_position = 0;

  /* dummy node has no label text */
  if (message == (char *) 0)
    {

      cairo_set_source_rgb (crp, 1, 0, 0);
      cairo_move_to (crp, x0, y0);
      cairo_rectangle (crp, x0, y0, 5, 5);
      cairo_fill (crp);
      return;
    }

  for (i = 0; i < 2; i++)
    {

      /* regular node or edgelabel node */
      r = (color & 0x00ff0000) >> 16;
      g = (color & 0x0000ff00) >> 8;
      b = (color & 0x000000ff);

      dpwi = un->tx;
      dphi = un->ty;

      if (edgelabel)
	{
	  /* edgelabel has background color of the graph and no shape */
	  xo = 0;
	  yo = 0;
	}
      else
	{

	  /* black border */
	  cairo_set_source_rgb (crp, 0, 0, 0);

	  /* node shapes */
	  switch (un->shape)
	    {
	    case NSHAPE_ELLIPS:
	      cairo_new_path (crp);
	      cairo_save (crp);
	      cairo_translate (crp,	/* ellipse->center_x */
			       x0 + (un->nx / 2),
			       /* ellipse->center_y */ y0 + (un->ny / 2));
	      cairo_scale (crp, /* ellipse->radius_x */ dpwi,
			   /* ellipse->radius_y */ dphi * 0.8);
	      cairo_arc (crp, 0.0, 0.0, 1.0, 0.0, 2.0 * M_PI);
	      cairo_restore (crp);
	      break;
	    case NSHAPE_CIRCLE:
	      cairo_new_path (crp);
	      cairo_save (crp);
	      cairo_translate (crp,	/* ellipse->center_x */
			       x0 + (un->nx / 2),
			       /* ellipse->center_y */ y0 + (un->ny / 2));
	      cairo_scale (crp, /* ellipse->radius_x */ un->nx / 2,
			   /* ellipse->radius_y */ un->ny / 2);
	      cairo_arc (crp, 0.0, 0.0, 1.0, 0.0, 2.0 * M_PI);
	      cairo_restore (crp);
	      break;
	    case NSHAPE_RBOX:
	      rbaspect = 1.0;	/* aspect ratio */
	      rbcorner_radius = un->ny / 10.0;	/* and corner curvature radius */
	      rbradius = rbcorner_radius / rbaspect;
	      rbdegrees = M_PI / 180.0;
	      cairo_new_sub_path (crp);
	      cairo_arc (crp, x0 + un->nx - rbradius, y0 + rbradius, rbradius,
			 -90 * rbdegrees, 0 * rbdegrees);
	      cairo_arc (crp, x0 + un->nx - rbradius, y0 + un->ny - rbradius,
			 rbradius, 0 * rbdegrees, 90 * rbdegrees);
	      cairo_arc (crp, x0 + rbradius, y0 + un->ny - rbradius, rbradius,
			 90 * rbdegrees, 180 * rbdegrees);
	      cairo_arc (crp, x0 + rbradius, y0 + rbradius, rbradius,
			 180 * rbdegrees, 270 * rbdegrees);
	      cairo_close_path (crp);
	      break;
	    case NSHAPE_BOX:
	    default:
	      cairo_rectangle (crp, x0, y0, un->nx, un->ny);
	      break;
	    }

	  cairo_stroke (crp);

	  /* background color of node */
	  cairo_set_source_rgb (crp, r / 255.0, g / 255.0, b / 255.0);

	  /* node shapes */
	  switch (un->shape)
	    {
	    case NSHAPE_ELLIPS:
	      cairo_new_path (crp);
	      cairo_save (crp);
	      cairo_translate (crp,	/* ellipse->center_x */
			       x0 + (un->nx / 2),
			       /* ellipse->center_y */ y0 + (un->ny / 2));
	      cairo_scale (crp, /* ellipse->radius_x */ dpwi,
			   /* ellipse->radius_y */ dphi * 0.8);
	      cairo_arc (crp, 0.0, 0.0, 1.0, 0.0, 2.0 * M_PI);
	      cairo_fill (crp);
	      cairo_restore (crp);
	      break;
	    case NSHAPE_CIRCLE:
	      cairo_new_path (crp);
	      cairo_save (crp);
	      cairo_translate (crp,	/* ellipse->center_x */
			       x0 + (un->nx / 2),
			       /* ellipse->center_y */ y0 + (un->ny / 2));
	      cairo_scale (crp, /* ellipse->radius_x */ un->nx / 2,
			   /* ellipse->radius_y */ un->ny / 2);
	      cairo_arc (crp, 0.0, 0.0, 1.0, 0.0, 2.0 * M_PI);
	      cairo_fill (crp);
	      cairo_restore (crp);
	      break;
	    case NSHAPE_RBOX:
	      rbaspect = 1.0;	/* aspect ratio */
	      rbcorner_radius = un->ny / 10.0;	/* and corner curvature radius */
	      rbradius = rbcorner_radius / rbaspect;
	      rbdegrees = M_PI / 180.0;
	      cairo_new_sub_path (crp);
	      cairo_arc (crp, x0 + un->nx - rbradius, y0 + rbradius, rbradius,
			 -90 * rbdegrees, 0 * rbdegrees);
	      cairo_arc (crp, x0 + un->nx - rbradius, y0 + un->ny - rbradius,
			 rbradius, 0 * rbdegrees, 90 * rbdegrees);
	      cairo_arc (crp, x0 + rbradius, y0 + un->ny - rbradius, rbradius,
			 90 * rbdegrees, 180 * rbdegrees);
	      cairo_arc (crp, x0 + rbradius, y0 + rbradius, rbradius,
			 180 * rbdegrees, 270 * rbdegrees);
	      cairo_close_path (crp);
	      cairo_fill (crp);
	      break;
	    case NSHAPE_BOX:
	    default:
	      cairo_rectangle (crp, x0, y0, un->nx, un->ny);
	      cairo_fill (crp);
	      break;
	    }

	  cairo_stroke (crp);


	}

      /* configure the label text color */
      tr = (un->textcolor & 0x00ff0000) >> 16;
      tg = (un->textcolor & 0x0000ff00) >> 8;
      tb = (un->textcolor & 0x000000ff);
      cairo_set_source_rgb (crp, tr, tg, tb);

      /* node shapes */
      switch (un->shape)
	{
	case NSHAPE_ELLIPS:
	  xo = un->nx / 4;
	  yo = un->ty / 4;
	  break;
	case NSHAPE_CIRCLE:
	  xo = (un->nx / 2) - (un->tx / 2);
	  yo = (un->ny / 2) - (un->ty / 2);
	  break;
	case NSHAPE_BOX:
	case NSHAPE_RBOX:
	default:
	  xo = drawing_default_textborder / 4;
	  yo = drawing_default_textborder / 4;
	  break;
	}

      if (edgelabel)
	{
	  /* edgelabel has background color of the graph and no shape */
	  xo = 0;
	  yo = 0;
	}


      /* configure the label text slant */
      textslant = CAIRO_FONT_SLANT_NORMAL;
      if (un->bitflags.textitalic)
	{
	  textslant = CAIRO_FONT_SLANT_ITALIC;
	}
      if (un->bitflags.textoblique)
	{
	  textslant = CAIRO_FONT_SLANT_OBLIQUE;
	}

      /* configure the label text weight */
      textweight = CAIRO_FONT_WEIGHT_NORMAL;
      if (un->bitflags.textbold)
	{
	  textweight = CAIRO_FONT_WEIGHT_BOLD;
	}

      /* configure the font name */
      if (un->fontname)
	{
	  cairo_select_font_face (crp, un->fontname, textslant, textweight);
	}
      else
	{
	  cairo_select_font_face (crp, drawing_default_fontname, textslant,
				  textweight);
	}
      /* configure the size of the font if set */
      if (un->fontsize)
	{
	  cairo_set_font_size (crp, un->fontsize);
	}
      else
	{
	  /* gtk default */
	  cairo_set_font_size (crp, drawing_default_fontsize);
	}

      dpwi = 0;
      dphi = 0;
      unty = y0;
      memset (buf, 0, 1024);
      ibuf = 0;
      cptr = message;
      textxsize = 0;
      textysize = 0;

      /* output per line */
      while (*cptr)
	{
	  memset (buf, 0, 1024);
	  ibuf = 0;
	  while (*cptr)
	    {
	      if (*cptr == '\n')
		{
		  cptr++;
		  break;
		}
	      buf[ibuf] = *cptr;
	      ibuf = ibuf + 1;
	      if (ibuf > 1020)
		{
		  break;
		}
	      cptr++;
	    }

	  cairo_text_extents (crp, buf, &extents);
	  dpwi = (int) extents.width;
	  dphi = (int) extents.height;
	  unty = unty + dphi;

	  if (dpwi > textxsize)
	    {
	      textxsize = dpwi;
	    }
	  textysize = textysize + dphi;

	  cairo_move_to (crp, x0 + xo, unty + yo);
	  cairo_show_text (crp, buf);

	}

      dpwi = un->tx;
      dphi = un->ty;

      un->tx = textxsize;
      un->ty = textysize;

      if (dpwi == textxsize && dphi == textysize)
	{
	  break;
	}
      redo_position = 1;
    }

  return;
}

/* init text sizes of a list of nodes */
static void
textsizes (struct dln *nodelist)
{
  cairo_surface_t *surface;
  cairo_t *crp;
  struct dln *dlptr;
  char *message = NULL;

  if (nodelist == (struct dln *) 0)
    {
      return;
    }

  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 100, 100);
  crp = cairo_create (surface);

  dlptr = nodelist;
  while (dlptr)
    {
      message = dlptr->un->label;
      if (message == NULL)
	{
	  dlptr->un->tx = 0;
	  dlptr->un->ty = 0;
	  dlptr->un->nx = 0;
	  dlptr->un->ny = 0;
	  dlptr = dlptr->next;
	  continue;
	}
      if (dlptr->un->tx == 0 && dlptr->un->ty == 0)
	{
	  textsizeun (dlptr->un, crp, surface);
	}
      dlptr = dlptr->next;
    }
  cairo_destroy (crp);
  cairo_surface_destroy (surface);
  return;
}

/* init text size in pixels of one node */
static void
textsizeun (struct unode *un, cairo_t * incrp, cairo_surface_t * insurface)
{
  cairo_font_weight_t textweight;
  cairo_font_slant_t textslant;
  cairo_text_extents_t extents;
  cairo_surface_t *surface;
  cairo_t *crp;
  char buf[1024];
  int ibuf = 0;
  char *cptr = NULL;
  gint dpwi = 0;
  gint dphi = 0;
  char *message = NULL;
  gdouble dy;

  if (incrp == NULL)
    {
      surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 100, 100);
      crp = cairo_create (surface);
    }
  else
    {
      surface = insurface;
      crp = incrp;
    }

  /* on screen text of node */
  message = un->label;

  /* null if dummy node */
  if (message == NULL)
    {
      un->tx = 0;
      un->ty = 0;
      un->nx = 0;
      un->ny = 0;
      return;
    }

  /* need to configure textsizes */
  if (un->tx == 0 && un->ty == 0)
    {
      memset (buf, 0, 1024);
      ibuf = 0;
      cptr = message;
      /* configure the label text slant */
      textslant = CAIRO_FONT_SLANT_NORMAL;
      if (un->bitflags.textitalic)
	{
	  textslant = CAIRO_FONT_SLANT_ITALIC;
	}
      if (un->bitflags.textoblique)
	{
	  textslant = CAIRO_FONT_SLANT_OBLIQUE;
	}

      /* configure the label text weight */
      textweight = CAIRO_FONT_WEIGHT_NORMAL;
      if (un->bitflags.textbold)
	{
	  textweight = CAIRO_FONT_WEIGHT_BOLD;
	}

      /* configure the font name */
      if (un->fontname)
	{
	  cairo_select_font_face (crp, un->fontname, textslant, textweight);
	}
      else
	{
	  cairo_select_font_face (crp, drawing_default_fontname,
				  textslant, textweight);
	}

      /* configure the size of the font if set */
      if (un->fontsize)
	{
	  cairo_set_font_size (crp, un->fontsize);
	}
      else
	{
	  /* gtk default */
	  cairo_set_font_size (crp, drawing_default_fontsize);
	}

      memset (buf, 0, 1024);
      ibuf = 0;

      while (*cptr)
	{
	  memset (buf, 0, 1024);
	  ibuf = 0;
	  while (*cptr)
	    {
	      if (*cptr == '\n')
		{
		  cptr++;
		  break;
		}
	      buf[ibuf] = *cptr;
	      ibuf = ibuf + 1;
	      if (ibuf > 1023)
		{
		  break;
		}
	      cptr++;
	    }
	  cairo_text_extents (crp, buf, &extents);
	  dpwi = (int) extents.width;
	  dphi = (int) extents.height;
	  if (dpwi > un->tx)
	    {
	      un->tx = dpwi;
	    }
	  un->ty = un->ty + dphi;
	}

      /* node shapes */
      switch (un->shape)
	{
	case NSHAPE_ELLIPS:
	  un->nx = 2 * un->tx;
	  dy = 2 * (un->ty * 0.8);
	  un->ny = (int) dy;
	  break;
	case NSHAPE_CIRCLE:
	  if (un->tx > un->ty)
	    {
	      un->nx = 1.4 * un->tx;
	      un->ny = 1.4 * un->tx;
	    }
	  else
	    {
	      un->nx = 1.4 * un->ty;
	      un->ny = 1.4 * un->ty;
	    }
	  break;
	case NSHAPE_BOX:
	case NSHAPE_RBOX:
	default:
	  un->nx = un->tx + drawing_default_textborder;
	  un->ny = un->ty + drawing_default_textborder;
	  break;
	}
    }

  if (incrp == NULL)
    {
      cairo_destroy (crp);
      cairo_surface_destroy (surface);
    }

  return;
}

/* */
static gboolean
on_mouse_clicked (GtkWidget * widget, GdkEventButton * event,
		  gpointer user_data)
{
  int i = 0;
  int w = 0;
  int h = 0;
  int hw = 0;
  int hh = 0;
  gdouble dhw = 0.0;
  struct usubg *subg = NULL;
  struct unode *un = NULL;
  struct dn *dptr;
  gdouble dx = 0.0;
  gdouble dy = 0.0;
  int nx = 0;
  int ny = 0;
  gdouble gdelta = 0.0;
  gdouble gsld = 0.0;
  gdouble val = 0.0;
  int ival = 0;

  if (widget)
    {
    }
  if (user_data)
    {
    }

  if (drawing == 0)
    {
      return TRUE;
    }

  if (option_debug)
    {
      printf ("%s(): mouse event %d\n", __FUNCTION__, (int) event->button);
    }
  /* both mouse buttons */
  if ((int) event->button == 2)
    {
      return TRUE;
    }
  /* right mouse button */
  if ((int) event->button == 3)
    {

      if (event->type == GDK_2BUTTON_PRESS)
	{
	  /* "double click\n" */
	  if (nodeinfo == NULL)
	    {

	      dx = ((int) event->x + vvxmin) / zzfactor;
	      dy = ((int) event->y + vvymin) / zzfactor;
	      nx = (int) dx;
	      ny = (int) dy;

	      /* scan the graph for node at clicked position */
	      un = NULL;
	      /* all nodes in all levels */
	      for (i = 0; i < nlevels; i++)
		{
		  dptr = levels[i];
		  while (dptr)
		    {
		      if (dptr->un)
			{
			  if (dptr->un->bitflags.edgelabel)
			    {
			      /* left mouse button click on edge label node */
			    }
			  else
			    {
			      if (dptr->un->label)
				{
				  if (nx >= dptr->x0 && ny >= dptr->y0)
				    {
				      if (nx <= (dptr->x0 + dptr->un->nx)
					  && ny <= (dptr->y0 + dptr->un->ny))
					{
					  /* left mouse button click node */
					  if (option_debug)
					    {
					      printf
						("clicked on node=%s label=%s\n",
						 dptr->un->name,
						 dptr->un->label);
					    }
					  un = dptr->un;
					  break;
					}
				    }
				}
			    }
			}
		      dptr = dptr->next;
		    }
		}
	      if (un)
		{
		  do_nodeinfo (un);
		}
	    }
	}
      else
	{
	  /* how large drawing area is */
	  (void) gdk_drawable_get_size (event->window, &w, &h);

	  /* center of drawing area */
	  hw = w / 2;
	  hh = h / 2;

	  /* */
	  dx = ((int) event->x + vvxmin) / zzfactor;
	  dy = ((int) event->y + vvymin) / zzfactor;
	  nx = (int) dx;
	  ny = (int) dy;

	  if (option_debug)
	    {
	      printf
		("click button=%d at(%d,%d) in drawing (%d,%d) center is (%d,%d)\n",
		 (int) event->button, (int) event->x, (int) event->y, nx, ny,
		 hw, hh);
	    }
	  if (nx < maxx)
	    {
	      dhw = (hw + vxmin) / zzfactor;
	      gdelta = dx - dhw;
	      vxmin = vxmin + (int) gdelta;
	      if (vxmin < 0)
		{
		  vxmin = 0;
		}
	      gsld = gdelta;
	      gsld = gdelta / maxx;
	      gsld = gsld * 100;
	      val = gtk_adjustment_get_value (GTK_ADJUSTMENT (adjhscale1));
	      ival = (int) val;
	      ival = ival + (int) gsld;
	      if (ival < 0)
		{
		  ival = 0;
		}
	      if (ival > 100)
		{
		  ival = 100;
		}
	      gtk_adjustment_set_value (GTK_ADJUSTMENT (adjhscale1), ival);
	    }
	  if (ny < maxy)
	    {
	      dhw = (hw + vymin) / zzfactor;
	      gdelta = dy - dhw;
	      vymin = vymin + (int) gdelta;
	      if (vymin < 0)
		{
		  vymin = 0;
		}
	      gsld = gdelta;
	      gsld = gdelta / maxy;
	      gsld = gsld * 100;
	      val = gtk_adjustment_get_value (GTK_ADJUSTMENT (adjvscale2));
	      ival = (int) val;
	      ival = ival + (int) gsld;
	      if (ival < 0)
		{
		  ival = 0;
		}
	      if (ival > 100)
		{
		  ival = 100;
		}
	      gtk_adjustment_set_value (GTK_ADJUSTMENT (adjvscale2), ival);
	    }
	  gtk_widget_queue_draw (drawingarea1);
	}
      return TRUE;
    }

  /* 1 == left mouse button */
  /* 1=left,2=both,3=right button */

  dx = ((int) event->x + vvxmin) / zzfactor;
  dy = ((int) event->y + vvymin) / zzfactor;
  nx = (int) dx;
  ny = (int) dy;

  /* 1=left,2=both,3=right button */
  if (option_debug)
    {
      printf ("click button=%d at(%d,%d) in drawing (%d,%d)\n",
	      (int) event->button, (int) event->x, (int) event->y, nx, ny);
    }
  /* scan the graph for node at clicked position */
  un = NULL;
  /* all nodes in all levels */
  for (i = 0; i < nlevels; i++)
    {
      dptr = levels[i];
      while (dptr)
	{
	  if (dptr->un)
	    {
	      if (dptr->un->bitflags.edgelabel)
		{
		  /* left mouse button click on edge label node */
		}
	      else
		{
		  if (dptr->un->label)
		    {
		      if (nx >= dptr->x0 && ny >= dptr->y0)
			{
			  if (nx <= (dptr->x0 + dptr->un->nx)
			      && ny <= (dptr->y0 + dptr->un->ny))
			    {
			      /* left mouse button click node */
			      if (option_debug)
				{
				  printf ("clicked on node=%s label=%s\n",
					  dptr->un->name, dptr->un->label);
				}
			      un = dptr->un;
			      break;
			    }
			}
		    }
		}
	    }
	  dptr = dptr->next;
	}
    }
  if (un)
    {
      /* clicked on real node */
      subg = un->rootedon;
      if (subg == NULL)
	{
	  /* node is in rootgraph and cannot be (un)folded */
	  if (option_debug)
	    {
	      printf ("node is rooted on rootgraph");
	    }
	  /* is a subgraph summary node */
	  if (un->bitflags.sumnode)
	    {
	      if (option_debug)
		{
		  printf ("this is a summary node\n");
		}
	      subg = find_subgraph_summaryname (un->name);
	      if (subg)
		{
		  /* toggle the subgraph folding status */
		  if (subg->bitflags.folded)
		    {
		      subg->bitflags.folded = 0;
		    }
		  else
		    {
		      subg->bitflags.folded = 1;
		    }
		}
	    }
	}
      else
	{
	  /* is a subgraph summary node */
	  if (un->bitflags.sumnode)
	    {
	      if (option_debug)
		{
		  printf ("this is a summary node\n");
		}
	      subg = find_subgraph_summaryname (un->name);
	      if (subg)
		{
		  /* toggle the subgraph folding status */
		  if (subg->bitflags.folded)
		    {
		      subg->bitflags.folded = 0;
		    }
		  else
		    {
		      subg->bitflags.folded = 1;
		    }
		}
	    }
	  else
	    {
	      /* toggle the subgraph folding status */
	      if (subg->bitflags.folded)
		{
		  subg->bitflags.folded = 0;
		}
	      else
		{
		  subg->bitflags.folded = 1;
		}
	    }
	  if (option_debug)
	    {
	      if (subg->bitflags.folded)
		{
		  printf
		    ("subgraph=%s is folded into summary node with label=%s\n",
		     subg->name, subg->summaryn->label);
		}
	      else
		{
		  printf ("subgraph=%s is unfolded\n", subg->name);
		}
	    }
	}
    }
  else
    {
      /* clicked on background or edgelabel or edgeline */
    }
  relayout ();
  mainlayout ();
  gtk_widget_queue_draw (drawingarea1);
  return TRUE;
}

static gboolean
on_mouse_wheel (GtkWidget * widget, GdkEventScroll * event,
		gpointer user_data)
{
  gdouble val = 0.0;

  if (widget)
    {
    }
  if (user_data)
    {
    }

  if (event->direction == GDK_SCROLL_UP)
    {
      val = gtk_adjustment_get_value (GTK_ADJUSTMENT (adjvscale2));
      if (val == 0)
	{
	  return TRUE;
	}
      val = val - 1;
      gtk_adjustment_set_value (GTK_ADJUSTMENT (adjvscale2), val);
      vymin = (int) ((val * maxy) / 100);
      gtk_widget_queue_draw (drawingarea1);
      return TRUE;
    }

  if (event->direction == GDK_SCROLL_DOWN)
    {
      val = gtk_adjustment_get_value (GTK_ADJUSTMENT (adjvscale2));
      if (val == 100)
	{
	  return TRUE;
	}
      val = val + 1;
      gtk_adjustment_set_value (GTK_ADJUSTMENT (adjvscale2), val);
      vymin = (int) ((val * maxy) / 100);
      gtk_widget_queue_draw (drawingarea1);
      return TRUE;
    }

  /* scroll left or scroll right is unused */
  return TRUE;
}

/* zoom scale left slider vertical */
static void
on_vscale1_changed (GtkAdjustment * adj)
{
  gdouble val0;
  int val1;
  if (drawing == 0)
    {
      return;
    }
  if (indraw)
    {
      return;
    }
  val0 = gtk_adjustment_get_value (adj);
  val1 = (int) val0;
  zfactor = exp ((double) (3 * (val1 - 50)) / (double) 50);
  if (option_debug)
    {
      printf ("%s(): slider=%d zoom=%f\n", __FUNCTION__, val1, zfactor);
    }
  redo_position = 1;
  gtk_widget_queue_draw (drawingarea1);

  /* flush gtk gui actions avoid gui lockup */
  while (gtk_events_pending ())
    {
      gtk_main_iteration ();
    }

  return;
}

/* vertical position offset */
static void
on_vscale2_changed (GtkAdjustment * adj)
{
  gdouble val;
  if (drawing == 0)
    {
      return;
    }
  if (indraw)
    {
      return;
    }
  val = gtk_adjustment_get_value (adj);
  vymin = (int) ((val * maxy) / 100);
  gtk_widget_queue_draw (drawingarea1);
  return;
}

/* horizontal position slider 0..100% of drawing */
static void
on_hscale1_changed (GtkAdjustment * adj)
{
  gdouble val;
  if (drawing == 0)
    {
      return;
    }
  if (indraw)
    {
      return;
    }
  val = gtk_adjustment_get_value (adj);
  vxmin = (int) ((val * maxx) / 100);
  gtk_widget_queue_draw (drawingarea1);
  return;
}

/* button1 fold all subgraphs at once */
static void
button1_clicked (GtkWidget * widget, gpointer window)
{
  struct usubg *sgp = NULL;

  if (widget)
    {
    }
  if (window)
    {
    }

  if (drawing == 0)
    {
      return;
    }
  if (indraw)
    {
      return;
    }
  /* nothing to unfold if there are no subgraphs */
  if (sgl == NULL)
    {
      return;
    }

  sgp = sgl;
  while (sgp)
    {
      sgp->bitflags.folded = 1;
      sgp = sgp->next;
    }

  relayout ();
  mainlayout ();
  gtk_widget_queue_draw (drawingarea1);

  return;
}

/* button2 unfold all subgraphs at once */
static void
button2_clicked (GtkWidget * widget, gpointer window)
{
  struct usubg *sgp = NULL;

  if (widget)
    {
    }
  if (window)
    {
    }

  if (drawing == 0)
    {
      return;
    }
  if (indraw)
    {
      return;
    }
  /* nothing to unfold if there are no subgraphs */
  if (sgl == NULL)
    {
      return;
    }
  sgp = sgl;
  while (sgp)
    {
      sgp->bitflags.folded = 0;
      sgp = sgp->next;
    }

  relayout ();
  mainlayout ();
  gtk_widget_queue_draw (drawingarea1);
  return;
}

/* button3 zoom to fit drawing in current screen */
static void
button3_clicked (GtkWidget * widget, gpointer window)
{
  double xzscale = 1.0;
  double yzscale = 1.0;
  double newzscale = 1.0;
  double dval = 1.0;
  int val = 0;

  if (widget)
    {
    }
  if (window)
    {
    }

  if (drawing == 0)
    {
      return;
    }
  if (indraw)
    {
      return;
    }

  xzscale = (double) (1000 * drawing_area_xsize / maxx);
  yzscale = (double) (1000 * drawing_area_ysize / maxy);
  xzscale = xzscale / 1000.0;
  yzscale = yzscale / 1000.0;

  if ((xzscale - yzscale) > 0)
    {
      newzscale = yzscale;
    }
  else
    {
      newzscale = xzscale;
    }

  if (option_debug)
    {
      printf
	("%s(): fit zoom to %f from xscale=%f and yscale=%f drawingarea=(%d,%d) maxy=(%d,%d)\n",
	 __FUNCTION__, newzscale, yzscale, xzscale, drawing_area_xsize,
	 drawing_area_ysize, maxx, maxy);
    }

  dval = log ((newzscale * (double) 50.0) / 3.0) - 50.0;
  dval = log (newzscale) / 3.0;
  dval = (dval * 50.0);
  dval = dval + 50.0;

  val = (int) dval;
  if (val < 0)
    {
      val = 0;
    }
  if (val > 100)
    {
      val = 100;
    }
  zfactor = exp ((double) (3 * (val - 50)) / (double) 50);
  gtk_adjustment_set_value (GTK_ADJUSTMENT (adjvscale1), val);

  if (option_debug)
    {
      printf ("%s(): new slider value is %d (dval=%f) zfactor=%f\n",
	      __FUNCTION__, val, dval, zfactor);
    }

  /* only a re-draw needed */
  gtk_widget_queue_draw (drawingarea1);
  return;
}

/* checkbox 1 is 'splines' */
static void
check1_toggle (GtkWidget * widget, gpointer window)
{
  if (window)
    {
    }
  if (drawing == 0)
    {
      return;
    }
  if (indraw)
    {
      return;
    }

  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
    {
      option_splines = 1;
    }
  else
    {
      option_splines = 0;
    }
  /* only a re-draw needed */
  gtk_widget_queue_draw (drawingarea1);
  return;
}

/* checkbox 2 is 'singlenodes' */
static void
check2_toggle (GtkWidget * widget, gpointer window)
{
  if (window)
    {
    }
  if (drawing == 0)
    {
      return;
    }
  if (indraw)
    {
      return;
    }

  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
    {
      option_no_singlenodes = 0;
    }
  else
    {
      option_no_singlenodes = 1;
    }

  relayout ();
  mainlayout ();
  gtk_widget_queue_draw (drawingarea1);
  return;
}

/* checkbox 3 is 'selfedges' */
static void
check3_toggle (GtkWidget * widget, gpointer window)
{
  if (window)
    {
    }
  if (drawing == 0)
    {
      return;
    }
  if (indraw)
    {
      return;
    }
  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
    {
      option_selfedges = 1;
    }
  else
    {
      option_selfedges = 0;
    }

  relayout ();
  mainlayout ();
  gtk_widget_queue_draw (drawingarea1);
  return;
}

/* */
static void
on_nodeinfo_window_destroy (GtkWidget * widget, gpointer data)
{

  if (widget)
    {
    }
  if (data)
    {
    }

  if (nodeinfo)
    {
      nodeinfo = NULL;
    }
  return;
}

/* Callback for close button */
static void
on_nodeinfo_window_button_clicked (GtkWidget * button, int window)
{
  if (button)
    {
    }
  if (window)
    {
    }
  if (nodeinfo)
    {
      gtk_widget_destroy (nodeinfo);
      nodeinfo = NULL;
    }
  return;
}

static void
do_nodeinfo (struct unode *un)
{
  GtkWidget *window1;
  GtkWidget *vbox;
  GtkWidget *text_view;
  GtkWidget *button;
  GtkTextBuffer *buffer;
  char buf[1024];
  char buf1[256];
  const char *textstring = "";
  struct dle *eptr;

  memset (buf, 0, 1024);
  memset (buf1, 0, 256);

  /* check if this is a folded subgraph summarynode */
  if (un->bitflags.sumnode)
    {
      snprintf (buf1, 256 - 1, "summarynodename: \"%s\"\n", un->name);
    }
  else
    {
      snprintf (buf1, 256 - 1, "nodename: \"%s\"\n", un->name);
    }
  strcat (buf, buf1);
  strcat (buf, "label: \"");
  memset (buf1, 0, 256);
  snprintf (buf1, 256 - 1, "%s\"\n", un->label);
  strcat (buf, buf1);
  strcat (buf, "\nedges:\n");
  eptr = wel;
  while (eptr)
    {
      if (eptr->ue->fn == un || eptr->ue->tn == un)
	{
	  memset (buf1, 0, 256);
	  snprintf (buf1, 256 - 1, "\"%s\"->\"%s\"\n", eptr->ue->fn->name,
		    eptr->ue->tn->name);
	  if (strlen (buf) + strlen (buf1) > 1023)
	    {
	      break;
	    }
	  strcat (buf, buf1);
	}
      eptr = eptr->next;
    }

  textstring = uniqstring (buf);

  window1 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position (GTK_WINDOW (window1), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size (GTK_WINDOW (window1), 250, 200);
  gtk_window_set_title (GTK_WINDOW (window1), "node info");
  g_signal_connect (G_OBJECT (window1), "destroy",
		    G_CALLBACK (on_nodeinfo_window_destroy), NULL);
  vbox = gtk_vbox_new (FALSE, 2);
  gtk_container_add (GTK_CONTAINER (window1), vbox);

  /* Create a multiline text widget. */
  text_view = gtk_text_view_new ();
  gtk_box_pack_start (GTK_BOX (vbox), text_view, 1, 1, 0);

  /* help text cannot be edited in the window */
  gtk_text_view_set_editable (GTK_TEXT_VIEW (text_view), FALSE);

  /* wrap by word */
  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view), GTK_WRAP_WORD);

  /* Obtaining the buffer associated with the widget. */
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));

  /* Set the default buffer text. */
  gtk_text_buffer_set_text (buffer, textstring, -1);

  /* Create a close button. */
  button = gtk_button_new_with_label ("Close");
  gtk_box_pack_start (GTK_BOX (vbox), button, 0, 0, 0);
  g_signal_connect (G_OBJECT (button), "clicked",
		    G_CALLBACK (on_nodeinfo_window_button_clicked),
		    (gpointer) 0);

  gtk_widget_show_all (window1);

  nodeinfo = window1;

  return;
}

/* help part */
static void on_help_window_destroy (GtkWidget * widget, gpointer data);
static void on_help_window_button_clicked (GtkWidget * button, int window);

/* */
static void
on_help_window_destroy (GtkWidget * widget, gpointer data)
{
  if (data)
    {
    }
  if (widget == window_helpgraph1)
    {
      window_helpgraph1 = NULL;
    }
  if (widget == window_helpsubgraph1)
    {
      window_helpsubgraph1 = NULL;
    }
  if (widget == window_helpnode1)
    {
      window_helpnode1 = NULL;
    }
  if (widget == window_helpedge1)
    {
      window_helpedge1 = NULL;
    }
  if (widget == window_helpsettings1)
    {
      window_helpsettings1 = NULL;
    }
  if (widget == window_helpoptions1)
    {
      window_helpoptions1 = NULL;
    }
  if (widget == window_helpgui1)
    {
      window_helpgui1 = NULL;
    }
  return;
}

/* Callback for close button */
static void
on_help_window_button_clicked (GtkWidget * button, int window)
{
  GtkWidget *window1 = NULL;
  if (button)
    {
    }
  switch (window)
    {
    case 1:
      window1 = window_helpgraph1;
      break;
    case 2:
      window1 = window_helpsubgraph1;
      break;
    case 3:
      window1 = window_helpnode1;
      break;
    case 4:
      window1 = window_helpedge1;
      break;
    case 5:
      window1 = window_helpsettings1;
      break;
    case 6:
      window1 = window_helpoptions1;
      break;
    case 7:
      window1 = window_helpgui1;
      break;
    default:
      window1 = NULL;
      break;
    }

  if (window1)
    {
      gtk_widget_destroy (window1);
    }

  switch (window)
    {
    case 1:
      window_helpgraph1 = NULL;
      break;
    case 2:
      window_helpsubgraph1 = NULL;
      break;
    case 3:
      window_helpnode1 = NULL;
      break;
    case 4:
      window_helpedge1 = NULL;
      break;
    case 5:
      window_helpsettings1 = NULL;
      break;
    case 6:
      window_helpoptions1 = NULL;
      break;
    case 7:
      window_helpgui1 = NULL;
      break;
    default:
      break;
    }

  return;
}


static void
on_top_level_window_do_help (long int window, const char *textwindow,
			     const char *text)
{
  GtkWidget *window1;
  GtkWidget *vbox;
  GtkWidget *text_view;
  GtkWidget *button;
  GtkTextBuffer *buffer;

  window1 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position (GTK_WINDOW (window1), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size (GTK_WINDOW (window1), 400, 200);
  gtk_window_set_title (GTK_WINDOW (window1), textwindow);
  g_signal_connect (G_OBJECT (window1), "destroy",
		    G_CALLBACK (on_help_window_destroy), NULL);
  vbox = gtk_vbox_new (FALSE, 2);
  gtk_container_add (GTK_CONTAINER (window1), vbox);

  /* Create a multiline text widget. */
  text_view = gtk_text_view_new ();
  gtk_box_pack_start (GTK_BOX (vbox), text_view, 1, 1, 0);

  /* help text cannot be edited in the window */
  gtk_text_view_set_editable (GTK_TEXT_VIEW (text_view), FALSE);

  /* wrap by word */
  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view), GTK_WRAP_WORD);

  /* Obtaining the buffer associated with the widget. */
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));

  /* Set the default buffer text. */
  gtk_text_buffer_set_text (buffer, text, -1);

  /* Create a close button. */
  button = gtk_button_new_with_label ("Close");
  gtk_box_pack_start (GTK_BOX (vbox), button, 0, 0, 0);
  g_signal_connect (G_OBJECT (button), "clicked",
		    G_CALLBACK (on_help_window_button_clicked),
		    (gpointer) window);

  gtk_widget_show_all (window1);


  switch (window)
    {
    case 1:
      window_helpgraph1 = window1;
      break;
    case 2:
      window_helpsubgraph1 = window1;
      break;
    case 3:
      window_helpnode1 = window1;
      break;
    case 4:
      window_helpedge1 = window1;
      break;
    case 5:
      window_helpsettings1 = window1;
      break;
    case 6:
      window_helpoptions1 = window1;
      break;
    case 7:
      window_helpgui1 = window1;
      break;
    default:
      break;
    }

  return;
}

static void
on_top_level_window_helpgraph1_activate (GtkMenuItem * menuitem,
					 gpointer user_data)
{
  const char *text_helpgraph1 =
    "A mooigraph graph starts always with\n"
    "  void graph (void)\n"
    "The structure of a mooigraph graph is\n"
    "  void graph (void) {\n"
    "    /* optional root graph settings */\n"
    "    settings (void) {\n"
    "      color=\"green\"\n"
    "    }\n"
    "    node(\"node1\");\n"
    "    node(\"node2\") {\n"
    "      /* optional node attributes */\n"
    "      color=\"blue\";\n"
    "    }\n"
    "    node(\"node3\");\n"
    "    edge(\"node1\",\"node2\");\n"
    "    edge(\"node1\",\"node3\") {\n"
    "      /* optional edge attributes */\n"
    "      color=\"red\";\n"
    "      style=\"dotted\";\n"
    "    }\n"
    "    subgraph(\"subgraph-1\") {\n"
    "      node(\"node4\");\n"
    "    }\n"
    "  }\n"
    "colornames are X11 rgb.txt colornames.\n"
    "mooigraph data files can be formatted\n"
    "using the indent software program.\n"
    "c-comments and c++ // comment style\n"
    "can be used in the graph anywhere.\n"
    "A string in mooigraph can use \"\\n\" or\n"
    "'\\n' to continue on the next line.\n"
    "names of nodes and subgraphs must be\n" "unique in the graph.\n";
  if (menuitem)
    {
    }
  if (user_data)
    {
    }
  if (window_helpgraph1)
    {
      return;
    }
  on_top_level_window_do_help (1, "help->graph", text_helpgraph1);
  return;
}


static void
on_top_level_window_helpsubgraph1_activate (GtkMenuItem * menuitem,
					    gpointer user_data)
{
  const char *text_helpsubgraph1 =
    "A subgraph must have a unique name in the graph\n"
    "and options can be set in a settings section.\n"
    "  subgraph(\"subgraph-name\")\n"
    "  {\n"
    "    settings (void)\n"
    "    {\n"
    "      folded=\"no\";\n"
    "      color=\"navyblue\";\n"
    "    }\n"
    "    node(\"node1\");\n"
    "    /* more nodes and edges here */\n"
    "  }\n"
    "The folded status in the settings section sets\n"
    "the initial folding status of the subgraph.\n"
    "The color set the color of the summary node when\n"
    "the subgraph is in folded status.\n";
  if (menuitem)
    {
    }
  if (user_data)
    {
    }
  if (window_helpsubgraph1)
    {
      return;
    }
  on_top_level_window_do_help (2, "help->subgraph", text_helpsubgraph1);
  return;
}


static void
on_top_level_window_helpnode1_activate (GtkMenuItem * menuitem,
					gpointer user_data)
{
  const char *text_helpnode1 =
    "A node must have a unique name in the graph\n"
    "which is used in the edge(\"from\",\"to\");\n"
    "A node without node attributes is defined:\n"
    "  node(\"nodename\");\n"
    "A node with node attributes can have these options:\n"
    "  node(\"nodename\")\n"
    "  {\n"
    "    label=\"displayname\";\n"
    "    shape=\"shapename\";\n"
    "    fontname=\"name-of-font\";\n"
    "    textslant=\"slant\";\n"
    "    textweight=\"weight\";\n"
    "    fonsize=\"number\";\n"
    "    color=\"colorname\";\n"
    "    textcolor=\"colorname\";\n"
    "  }\n"
    "label is the text seen on screen for the node.\n"
    "shape is \"box\", \"rbox\", \"circle\" or \"ellips\".\n"
    "fontname is the name of the font to use.\n"
    "textslant is \"italic\" or \"oblique\" or\n"
    "something else to not use a slant.\n"
    "textweight is \"bold\" or something else\n"
    "to not apply a textweight to the label text.\n"
    "fontsize must be >1 and <100 example \"20\"\n"
    "and gtk default font size is 10 poins.\n"
    "color is the background color of the node.\n"
    "textcolor is the color for the label text.\n";
  if (menuitem)
    {
    }
  if (user_data)
    {
    }
  if (window_helpnode1)
    {
      return;
    }
  on_top_level_window_do_help (3, "help->node", text_helpnode1);
  return;
}

static void
on_top_level_window_helpedge1_activate (GtkMenuItem * menuitem,
					gpointer user_data)
{
  const char *text_helpedge1 =
    "A edge connects two node with each other.\n"
    "The node names do not need to be defined\n"
    "before the edge definition.\n"
    "An edge without attributes is specified:\n"
    "  edge(\"from-node-name\",\"to-node-name\");\n"
    "An edge with attributes can have these options:\n"
    "  edge(\"from-node\",\"to-node\")\n"
    "  {\n"
    "    color=\"colorname\";\n"
    "    textcolor=\"colorname\";\n"
    "    label=\"label-text\";\n"
    "    fontname=\"name-of-font\";\n"
    "    textslant=\"slant\";\n"
    "    textweight=\"weight\";\n"
    "    fontsize=\"number\";\n"
    "    style=\"line-style\";\n"
    "  }\n"
    "color is the color of the edge line.\n"
    "textcolor is the color of the label text.\n"
    "label is the text between the nodes.\n"
    "fontname is the font to use. default \"serif\"\n"
    "textslant is \"italic\", \"oblique\" or\n"
    "something else to not use a text slant.\n"
    "textweight is \"bold\" or something else to not\n"
    "use a weight on the text for the label.\n"
    "fontsize is a number >1 and <100, example \"20\".\n"
    "style is the style of the edge line to draw.\n"
    "style options are \"normal\", \"solid\", \"dotted\"\n"
    "\"dashed\" or \"invis\" for an invisible edge.\n";
  if (menuitem)
    {
    }
  if (user_data)
    {
    }
  if (window_helpedge1)
    {
      return;
    }
  on_top_level_window_do_help (4, "help->edge", text_helpedge1);
  return;
}

static void
on_top_level_window_helpsettings1_activate (GtkMenuItem * menuitem,
					    gpointer user_data)
{
  const char *text_helpsettings1 =
    "Settings is optional and sets options for a\n"
    "whole toplevel graph or for a subgraph;\n"
    "Settings has these options available:\n"
    "  settings (void)\n"
    "  {\n"
    "    color=\"colorname\";\n"
    "    folded=\"yes-or-no\";\n"
    "  }\n"
    "If in the root graph color specifies the\n"
    "color of the background of the drawing.\n"
    "If in a subgraph color specifies the color\n"
    "of the summary node when in folded state.\n"
    "folded is \"yes\" or \"no\" in a subgraph\n"
    "to set the initial folding subgraph status.\n"
    "There are also commandline options to set\n"
    "the initial folding status of the subgraphs\n"
    "and the \"fold\", \"unfold\" buttons will\n"
    "fold or unfold all subgraphs at once.\n";
  if (menuitem)
    {
    }
  if (user_data)
    {
    }
  if (window_helpsettings1)
    {
      return;
    }
  on_top_level_window_do_help (5, "help->settings", text_helpsettings1);
  return;
}


static void
on_top_level_window_helpoptions1_activate (GtkMenuItem * menuitem,
					   gpointer user_data)
{
  const char *text_helpoptions1 =
    "This are the commandline options using the --help option:\n"
    "\n"
    "mooigraph [--options] [inputfile]\n"
    "input file can be graphviz dot file or mooigraph format file\n"
    "options are one or more of these options:\n"
    "--noedgelabels  do not draw edge labels\n"
    "--edgelabels    do draw edge labels\n"
    "--nosinglenodes do not draw unconnected single nodes\n"
    "--singlenodes   do draw unconnected single nodes\n"
    "--nosplines     draw normal edge lines\n"
    "--splines       draw spline edge lines\n"
    "--selfedges     do draw self edges\n"
    "--noselfedges   do not draw self edges\n"
    "--folded        fold all subgraphs initially\n"
    "--unfolded      unfold all subgraphs initially\n"
    "--colortable    generate html page with table of color names\n"
    "--nodenames     show node names not labels in the drawing\n"
    "--lmgdebug      print libmooigraph debug info to stdout\n"
    "--debug         print debug info to stdout\n"
    "--version       print version info\n"
    "--help          print this option help\n"
    "\n"
    "self edges are edges with same from and to node.\n"
    "single nodes have no connection to other nodes.\n";
  if (menuitem)
    {
    }
  if (user_data)
    {
    }
  if (window_helpoptions1)
    {
      return;
    }
  on_top_level_window_do_help (6, "help->options", text_helpoptions1);
  return;
}


static void
on_top_level_window_helpgui1_activate (GtkMenuItem * menuitem,
				       gpointer user_data)
{
  const char *text_helpgui1 =
    "mouse left button click on a node does fold or unfold\n"
    "the subgraph if the node is part of a subgraph.\n"
    "root nodes are always visible.\n"
    "mouse right button click does center that point on screen\n"
    "and is a usable way to scroll through the drawing.\n"
    "mouse right button double click opens a window with\n"
    "node information if the mouse was above a node.\n"
    "for the check boxes:\n"
    "single node is a node without connections to other nodes\n"
    "self edges are edges with same origin and destination\n"
    "the text window says the number of nodes and edges\n"
    "in the drawing with the 1:1 scale of the drawing (x,y)\n"
    "The mouse wheel can be used to scroll up/down in the drawing\n";
  if (menuitem)
    {
    }
  if (user_data)
    {
    }
  if (window_helpgui1)
    {
      return;
    }
  on_top_level_window_do_help (7, "help->gui", text_helpgui1);
  return;
}

static void
on_top_level_window_saveaspng1_activate (GtkMenuItem * menuitem,
					 gpointer user_data)
{
  int dosplines = 0;
  GtkWidget *dialog = (GtkWidget *) 0;
  char *file_chooser_filename = (char *) 0;
  char *pngfilename = NULL;
  cairo_surface_t *surface;
  cairo_t *crp;
  GtkFileChooser *chooser;
  GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
  gint res;
  int mymaxx = 0;
  int mymaxy = 0;

  if (menuitem)
    {
    }
  if (user_data)
    {
    }

  if (drawing == 0)
    {
      return;
    }

  dialog = gtk_file_chooser_dialog_new ("Save As Png",
					/* parent_window */ 0,
					action,
					"_Cancel",
					GTK_RESPONSE_CANCEL,
					"_Save", GTK_RESPONSE_ACCEPT, NULL);
  chooser = GTK_FILE_CHOOSER (dialog);
  if (lastdir)
    {
      gtk_file_chooser_set_current_folder (chooser, lastdir);
    }

  gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE);

  res = gtk_dialog_run (GTK_DIALOG (dialog));

  if (res == GTK_RESPONSE_ACCEPT)
    {
      file_chooser_filename = gtk_file_chooser_get_filename (chooser);
      lastdir = gtk_file_chooser_get_current_folder (chooser);
    }
  else
    {
      /* cancel button */
      (void) gtk_widget_destroy (dialog);
      return;
    }

  /* */
  (void) gtk_widget_destroy (dialog);

  /* */
  if (file_chooser_filename)
    {

      pngfilename = strdup (file_chooser_filename);
      /* */
      (void) g_free (file_chooser_filename);
    }
  else
    {
      return;
    }

  dosplines = option_splines;
  vvxmin = vxmin;
  vvymin = vymin;
  zzfactor = zfactor;

  mymaxx = (int) (maxx * zzfactor);
  mymaxy = (int) (maxy * zzfactor);

  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, mymaxx, mymaxy);
  crp = cairo_create (surface);

  /* fill drawing background with background color */
  cairo_set_source_rgb (crp, bgcr / 255.0, bgcg / 255.0, bgcb / 255.0);
  cairo_rectangle (crp, 0, 0, mymaxx, mymaxy);
  cairo_fill (crp);

  /* use zoom slider drawing scale */
  cairo_scale (crp, zzfactor, zzfactor);

  draw_nodes (crp);
  draw_edges (crp, dosplines);

  cairo_surface_write_to_png (surface, pngfilename);

  mgr_free (pngfilename);

  cairo_destroy (crp);
  cairo_surface_destroy (surface);

  return;
}

static void
on_top_level_window_saveassvg1_activate (GtkMenuItem * menuitem,
					 gpointer user_data)
{
  int dosplines = 0;
  GtkWidget *dialog = (GtkWidget *) 0;
  char *file_chooser_filename = (char *) 0;
  char *svgfilename = NULL;
  cairo_surface_t *surface;
  cairo_t *crp;
  GtkFileChooser *chooser;
  GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
  gint res;
  int mymaxx = 0;
  int mymaxy = 0;

  if (menuitem)
    {
    }
  if (user_data)
    {
    }

  if (drawing == 0)
    {
      return;
    }

  dialog = gtk_file_chooser_dialog_new ("Save As Svg",
					/* parent_window */ 0,
					action,
					"_Cancel",
					GTK_RESPONSE_CANCEL,
					"_Save", GTK_RESPONSE_ACCEPT, NULL);
  chooser = GTK_FILE_CHOOSER (dialog);
  if (lastdir)
    {
      gtk_file_chooser_set_current_folder (chooser, lastdir);
    }

  gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE);

  res = gtk_dialog_run (GTK_DIALOG (dialog));


  if (res == GTK_RESPONSE_ACCEPT)
    {
      file_chooser_filename = gtk_file_chooser_get_filename (chooser);
      lastdir = gtk_file_chooser_get_current_folder (chooser);
    }
  else
    {
      /* cancel button */
      (void) gtk_widget_destroy (dialog);
      return;
    }

  /* */
  (void) gtk_widget_destroy (dialog);

  /* */
  if (file_chooser_filename)
    {

      svgfilename = strdup (file_chooser_filename);
      /* */
      (void) g_free (file_chooser_filename);
    }
  else
    {
      return;
    }

  dosplines = option_splines;
  vvxmin = vxmin;
  vvymin = vymin;
  zzfactor = zfactor;

  mymaxx = (int) (maxx * zzfactor);
  mymaxy = (int) (maxy * zzfactor);

  surface = cairo_svg_surface_create (svgfilename, mymaxx, mymaxy);
  crp = cairo_create (surface);

  /* fill drawing background with background color */
  cairo_set_source_rgb (crp, bgcr / 255.0, bgcg / 255.0, bgcb / 255.0);
  cairo_rectangle (crp, 0, 0, mymaxx, mymaxy);
  cairo_fill (crp);

  /* use zoom slider drawing scale */
  cairo_scale (crp, zzfactor, zzfactor);

  draw_nodes (crp);
  draw_edges (crp, dosplines);

  mgr_free (svgfilename);

  cairo_destroy (crp);
  cairo_surface_destroy (surface);

  return;
}

static void
on_top_level_window_saveaspdf1_activate (GtkMenuItem * menuitem,
					 gpointer user_data)
{
  int dosplines = 0;
  GtkWidget *dialog = (GtkWidget *) 0;
  char *file_chooser_filename = (char *) 0;
  char *pdffilename = NULL;
  cairo_surface_t *surface;
  cairo_t *crp;
  GtkFileChooser *chooser;
  GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
  gint res;
  int mymaxx = 0;
  int mymaxy = 0;

  if (menuitem)
    {
    }
  if (user_data)
    {
    }

  if (drawing == 0)
    {
      return;
    }

  dialog = gtk_file_chooser_dialog_new ("Save File",
					/* parent_window */ 0,
					action,
					"_Cancel",
					GTK_RESPONSE_CANCEL,
					"_Save", GTK_RESPONSE_ACCEPT, NULL);
  chooser = GTK_FILE_CHOOSER (dialog);
  if (lastdir)
    {
      gtk_file_chooser_set_current_folder (chooser, lastdir);
    }

  gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE);

  res = gtk_dialog_run (GTK_DIALOG (dialog));

  if (res == GTK_RESPONSE_ACCEPT)
    {
      file_chooser_filename = gtk_file_chooser_get_filename (chooser);
      lastdir = gtk_file_chooser_get_current_folder (chooser);
    }
  else
    {
      /* cancel button */
      (void) gtk_widget_destroy (dialog);
      return;
    }

  /* */
  (void) gtk_widget_destroy (dialog);

  /* */
  if (file_chooser_filename)
    {

      pdffilename = strdup (file_chooser_filename);
      /* */
      (void) g_free (file_chooser_filename);
    }
  else
    {
      return;
    }

  dosplines = option_splines;
  vvxmin = vxmin;
  vvymin = vymin;
  zzfactor = zfactor;

  mymaxx = (int) (maxx * zzfactor);
  mymaxy = (int) (maxy * zzfactor);

  surface = cairo_pdf_surface_create (pdffilename, mymaxx, mymaxy);
  crp = cairo_create (surface);

  /* fill drawing background with background color */
  cairo_set_source_rgb (crp, bgcr / 255.0, bgcg / 255.0, bgcb / 255.0);
  cairo_rectangle (crp, 0, 0, mymaxx, mymaxy);
  cairo_fill (crp);

  /* use zoom slider drawing scale */
  cairo_scale (crp, zzfactor, zzfactor);

  draw_nodes (crp);
  draw_edges (crp, dosplines);

  cairo_show_page (crp);

  mgr_free (pdffilename);

  cairo_destroy (crp);
  cairo_surface_destroy (surface);

  return;
}

/* save as tulip software tlp format */
static void
on_top_level_window_saveastulip1_activate (GtkMenuItem * menuitem,
					   gpointer user_data)
{
  GtkWidget *dialog = (GtkWidget *) 0;
  char *file_chooser_filename = (char *) 0;
  char *tulipfilename = NULL;
  GtkFileChooser *chooser;
  GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
  gint res;
  FILE *f = NULL;
  struct dln *nptr;
  struct dle *eptr;
  int edgecount = 0;
  char *s = NULL;

  if (menuitem)
    {
    }
  if (user_data)
    {
    }

  if (drawing == 0)
    {
      return;
    }

  dialog = gtk_file_chooser_dialog_new ("Save File",
					/* parent_window */ 0,
					action,
					"_Cancel",
					GTK_RESPONSE_CANCEL,
					"_Save", GTK_RESPONSE_ACCEPT, NULL);
  chooser = GTK_FILE_CHOOSER (dialog);
  if (lastdir)
    {
      gtk_file_chooser_set_current_folder (chooser, lastdir);
    }

  gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE);

  res = gtk_dialog_run (GTK_DIALOG (dialog));

  if (res == GTK_RESPONSE_ACCEPT)
    {
      file_chooser_filename = gtk_file_chooser_get_filename (chooser);
      lastdir = gtk_file_chooser_get_current_folder (chooser);
    }
  else
    {
      /* cancel button */
      (void) gtk_widget_destroy (dialog);
      return;
    }

  /* */
  (void) gtk_widget_destroy (dialog);

  /* */
  if (file_chooser_filename)
    {
      tulipfilename = strdup (file_chooser_filename);
      /* */
      (void) g_free (file_chooser_filename);
    }
  else
    {
      return;
    }
  f = fopen (tulipfilename, "w");
  if (f)
    {
      fprintf (f, "(tlp \"2.0\"\n");
      fprintf (f, "(date \"09-11-2006\")\n");
      fprintf (f, "(author \"%s\")\n", PACKAGE_NAME);
      fprintf (f, "(comments \"This file was generated by %s\")\n",
	       PACKAGE_STRING);

      /* nodes in the drawing */
      if (wnl)
	{
	  fprintf (f, "(nodes ");
	  nptr = wnl;
	  while (nptr)
	    {
	      fprintf (f, "%d ", nptr->un->number);
	      nptr = nptr->next;
	    }
	  fprintf (f, ")\n");
	}

      /* edges in the drawing */
      if (wel)
	{
	  eptr = wel;
	  edgecount = 1;
	  while (eptr)
	    {
	      fprintf (f, "(edge %d %d %d)\n", edgecount,
		       eptr->ue->fn->number, eptr->ue->tn->number);
	      edgecount = edgecount + 1;
	      eptr = eptr->next;
	    }

	  fprintf (f, ")\n");
	}

      /* node labels */
      if (wnl)
	{
	  fprintf (f, "(property  0 string \"viewLabel\"\n");
	  fprintf (f, "(default \"\" \"\")\n");
	  nptr = wnl;
	  while (nptr)
	    {
	      fprintf (f, "(node %d ", nptr->un->number);
	      if (nptr->un->label)
		{
		  s = nptr->un->label;
		  fputc ('"', f);
		  while (*s)
		    {
		      if (*s == '\n')
			{
			  fputc ('\\', f);
			  fputc ('n', f);
			}
		      else if (*s == '"')
			{
			  fputc ('\\', f);
			  fputc ('"', f);
			}
		      else
			{
			  fputc (*s, f);
			}
		      s++;
		    }
		  fputc ('"', f);
		}
	      else
		{
		  fprintf (f, "\"\"");
		}
	      fprintf (f, ")\n");
	      nptr = nptr->next;
	    }
	  fprintf (f, ")\n");
	}

      /* ready */
      fclose (f);
    }

  mgr_free (tulipfilename);
  return;
}


static void
on_top_level_window_saveastys1_1node (FILE * f, struct unode *un)
{
  fprintf (f, "\tnode(\"%s\");\n", un->name);
  return;
}

static void
on_top_level_window_saveastys1_1edge (FILE * f, struct uedge *ue)
{
  fprintf (f, "\tedge(\"%s\",\"%s\");\n", ue->fn->name, ue->tn->name);
  return;
}

static void
on_top_level_window_saveastys1_activate (GtkMenuItem *
					 menuitem, gpointer user_data)
{
  GtkWidget *dialog = (GtkWidget *) 0;
  char *file_chooser_filename = (char *) 0;
  char *tysfilename = NULL;
  GtkFileChooser *chooser;
  GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
  gint res;
  FILE *f = NULL;
  struct dln *nptr;
  struct dle *eptr;
  int dopr = 0;

  if (menuitem)
    {
    }
  if (user_data)
    {
    }

  if (drawing == 0)
    {
      return;
    }

  dialog = gtk_file_chooser_dialog_new ("Save File",
					/* parent_window */
					0,
					action,
					"_Cancel",
					GTK_RESPONSE_CANCEL,
					"_Save", GTK_RESPONSE_ACCEPT, NULL);
  chooser = GTK_FILE_CHOOSER (dialog);
  if (lastdir)
    {
      gtk_file_chooser_set_current_folder (chooser, lastdir);
    }

  gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE);
  res = gtk_dialog_run (GTK_DIALOG (dialog));
  if (res == GTK_RESPONSE_ACCEPT)
    {
      file_chooser_filename = gtk_file_chooser_get_filename (chooser);
      lastdir = gtk_file_chooser_get_current_folder (chooser);
    }
  else
    {
      /* cancel button */
      (void) gtk_widget_destroy (dialog);
      return;
    }

  /* */
  (void) gtk_widget_destroy (dialog);
  /* */
  if (file_chooser_filename)
    {
      tysfilename = strdup (file_chooser_filename);
      /* */
      (void) g_free (file_chooser_filename);
    }
  else
    {
      return;
    }
  f = fopen (tysfilename, "w");
  if (f)
    {
      fprintf (f, "void graph (void)\n{\n");
      dopr = 0;
      /* first the nodes without edge connections */
      nptr = wnl;
      while (nptr)
	{
	  if (nptr->un->bitflags.hasin == 0 && nptr->un->bitflags.hasout == 0)
	    {
	      if (dopr == 0)
		{
		  fprintf (f, "\t/* nodes without edge connections */\n");
		  dopr = 1;
		}
	      on_top_level_window_saveastys1_1node (f, nptr->un);
	    }
	  nptr = nptr->next;
	}

      /* now the nodes without incoming edge connections and only outgoing edge connections */
      dopr = 0;
      nptr = wnl;
      while (nptr)
	{
	  if (nptr->un->bitflags.hasin == 0 && nptr->un->bitflags.hasout == 1)
	    {
	      if (dopr == 0)
		{
		  fprintf (f,
			   "\t/* nodes without incoming edge connections and only outgoing edge connections */\n");
		  dopr = 1;
		}
	      on_top_level_window_saveastys1_1node (f, nptr->un);
	    }
	  nptr = nptr->next;
	}

      /* now the nodes with incoming edge connections and outgoing edge connections */
      dopr = 0;
      nptr = wnl;
      while (nptr)
	{
	  if (nptr->un->bitflags.hasin == 1 && nptr->un->bitflags.hasout == 1)
	    {
	      if (dopr == 0)
		{
		  fprintf (f,
			   "\t/* nodes with incoming edge connections and outgoing edge connections */\n");
		  dopr = 1;
		}
	      on_top_level_window_saveastys1_1node (f, nptr->un);
	    }
	  nptr = nptr->next;
	}

      /* now the nodes with only incoming edge connections and without outgoing edge connections */
      dopr = 0;
      nptr = wnl;
      while (nptr)
	{
	  if (nptr->un->bitflags.hasin == 1 && nptr->un->bitflags.hasout == 0)
	    {
	      if (dopr == 0)
		{
		  fprintf (f,
			   "\t/* nodes with only incoming edge connections and without outgoing edge connections */\n");
		  dopr = 1;
		}
	      on_top_level_window_saveastys1_1node (f, nptr->un);
	    }
	  nptr = nptr->next;
	}
      fprintf (f, "\t/* edges */\n");
      eptr = wel;
      while (eptr)
	{
	  if (option_selfedges)
	    {
	      /* allow selfedges */
	      on_top_level_window_saveastys1_1edge (f, eptr->ue);
	    }
	  else
	    {
	      if (eptr->ue->bitflags.selfedge)
		{
		  /* skip selfedge */
		}
	      else
		{
		  on_top_level_window_saveastys1_1edge (f, eptr->ue);
		}
	    }
	  eptr = eptr->next;
	}
      fprintf (f, "}\n");
      fclose (f);
    }

  mgr_free (tysfilename);
  return;
}

/*
 * There are 5 pre-defined entity references in XML:
 * &lt; 	< 	less than
 * &gt; 	> 	greater than
 * &amp; 	& 	ampersand 
 * &apos; 	' 	apostrophe
 * &quot; 	" 	quotation mark
 * This is what dia uses for <>"'&#
 * <dia:string>#&lt;&gt;"'&amp;##</dia:string>
 */
static void
dia_string (FILE * f, char *string)
{
  char *p = NULL;

  if (string == NULL)
    {
      return;
    }

  p = string;
  while (*p)
    {
      if (*p == '#')
	{
	  /* dia uses # for # */
	  fputc ('#', f);
	}
      else if (*p == '\'')
	{
	  /* dia uses ' for ' */
	  fputc ('\'', f);
	}
      else if (*p == '"')
	{
	  /* dia uses " for " */
	  fputc ('"', f);
	}
      else if (*p == '<')
	{
	  /* dia uses xml < for < */
	  fputs ("&lt;", f);
	}
      else if (*p == '>')
	{
	  /* dia uses xml > for > */
	  fputs ("&gt;", f);
	}
      else if (*p == '&')
	{
	  /* dia uses xml & for & */
	  fputs ("amp;", f);
	}
      else
	{
	  fputc (*p, f);
	}
      p++;
    }

  return;
}

/* save as dia software xml format */
static void
on_top_level_window_saveasdia1_activate (GtkMenuItem * menuitem,
					 gpointer user_data)
{
  GtkWidget *dialog = (GtkWidget *) 0;
  char *file_chooser_filename = (char *) 0;
  char *diafilename = NULL;
  GtkFileChooser *chooser;
  GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
  gint res = 0;
  FILE *f = NULL;
  int i = 0;
  struct dn *dptr = NULL;
  struct de *eptr = NULL;
  int red = 0;
  int blue = 0;
  int green = 0;
  double diafactor = 10.0;
  int objectcount = 0;

  if (menuitem)
    {
    }
  if (user_data)
    {
    }

  if (drawing == 0)
    {
      return;
    }

  dialog = gtk_file_chooser_dialog_new ("Save File",
					/* parent_window */ 0,
					action,
					"_Cancel",
					GTK_RESPONSE_CANCEL,
					"_Save", GTK_RESPONSE_ACCEPT, NULL);
  chooser = GTK_FILE_CHOOSER (dialog);

  if (lastdir)
    {
      gtk_file_chooser_set_current_folder (chooser, lastdir);
    }

  gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE);

  res = gtk_dialog_run (GTK_DIALOG (dialog));

  if (res == GTK_RESPONSE_ACCEPT)
    {
      file_chooser_filename = gtk_file_chooser_get_filename (chooser);
      lastdir = gtk_file_chooser_get_current_folder (chooser);
    }
  else
    {
      /* cancel button */
      (void) gtk_widget_destroy (dialog);
      return;
    }

  /* */
  (void) gtk_widget_destroy (dialog);

  /* */
  if (file_chooser_filename)
    {
      diafilename = strdup (file_chooser_filename);
      /* */
      (void) g_free (file_chooser_filename);
    }
  else
    {
      return;
    }
  f = fopen (diafilename, "w");
  if (f)
    {
      /* scaling factor */
      diafactor = 10.0;

      /* uniq object counter */
      objectcount = 0;

      /* head */
      fprintf (f, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
      fprintf (f,
	       "<dia:diagram xmlns:dia=\"http://www.lysator.liu.se/~alla/dia/\">\n");
      fprintf (f,
	       "<dia:layer name=\"Background\" visible=\"true\" active=\"true\">\n");
      fprintf (f, "<!-- generated by %s %s -->\n", PACKAGE_STRING,
	       PACKAGE_URL);

      /* all nodes in all levels */
      for (i = 0; i < nlevels; i++)
	{

	  dptr = levels[i];
	  while (dptr)
	    {

	      if (dptr->un)
		{
		  if (dptr->un->label)
		    {
		      /* regular node or edge label */
		      fprintf (f,
			       "<!-- node %s in drawing at (%d,%d) size (%d,%d) relative position (%d,%d) -->\n",
			       dptr->un->name, dptr->x0, dptr->y0,
			       dptr->un->nx, dptr->un->ny, dptr->pos,
			       dptr->level);

		      fprintf (f,
			       "<dia:object type=\"Standard - Box\" version=\"0\" id=\"O%d\">\n",
			       objectcount);
		      /* dia object number to reference in edge */
		      dptr->un->objectnr = objectcount;
		      objectcount = objectcount + 1;
		      fprintf (f, "<dia:attribute name=\"obj_pos\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n",
			       dptr->x0 / diafactor, dptr->y0 / diafactor);
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"obj_bb\">\n");
		      fprintf (f,
			       "<dia:rectangle val=\"%f,%f;%f,%f\"/>\n",
			       dptr->x0 / diafactor,
			       dptr->y0 / diafactor,
			       (dptr->x0 + dptr->un->nx) / diafactor,
			       (dptr->y0 + dptr->un->ny) / diafactor);
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"elem_corner\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n",
			       dptr->x0 / diafactor, dptr->y0 / diafactor);
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"elem_width\">\n");
		      fprintf (f, "<dia:real val=\"%f\"/>\n",
			       dptr->un->nx / diafactor);
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name =\"elem_height\">\n");
		      fprintf (f, "<dia:real val=\"%f\"/>\n",
			       dptr->un->ny / diafactor);
		      fprintf (f, "</dia:attribute>\n");

		      red = (dptr->un->color & 0x00ff0000) >> 16;
		      green = (dptr->un->color & 0x0000ff00) >> 8;
		      blue = (dptr->un->color & 0x000000ff);
		      fprintf (f, "<dia:attribute name=\"inner_color\">\n");
		      fprintf (f, "<dia:color val=\"#%02x%02x%02x\"/>\n", red,
			       green, blue);
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f,
			       "<dia:attribute name=\"show_background\">\n");
		      fprintf (f, "<dia:boolean val=\"true\"/>\n");
		      fprintf (f, "</dia:attribute>\n");
		      fprintf (f, "</dia:object>\n");


		      fprintf (f,
			       "<dia:object type=\"Standard - Text\" version=\"1\" id=\"O%d\">\n",
			       objectcount);
		      objectcount = objectcount + 1;

		      fprintf (f, "<dia:attribute name=\"obj_pos\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n",
			       dptr->x0 / diafactor, dptr->y0 / diafactor);
		      fprintf (f, "</dia:attribute>\n");

		      /*
		       * fprintf (f, "<dia:attribute name=\"obj_bb\">\n");
		       * fprintf (f,"<dia:rectangle val=\"13.7,0.186375;14.5336,0.98725\"/>\n");
		       * fprintf (f, "</dia:attribute>\n");
		       */

		      fprintf (f, "<dia:attribute name=\"text\">\n");
		      fprintf (f, "<dia:composite type=\"text\">\n");
		      fprintf (f, "<dia:attribute name=\"string\">\n");
		      fprintf (f, "<dia:string>#");
		      dia_string (f, dptr->un->label);
		      fprintf (f, "#</dia:string>\n");
		      fprintf (f, "</dia:attribute>\n");

		      /* <dia:attribute name="font"> */
		      /* <dia:font family="sans" style="0" name="Helvetica"/> */
		      /* </dia:attribute> */

		      /* <dia:attribute name="height"> */
		      /* <dia:real val="0.80000000000000004"/> */
		      /* </dia:attribute> */

		      /* <dia:attribute name="pos"> */
		      /* <dia:point val="13.7,0.8"/> */
		      /* </dia:attribute> */

		      /* <dia:attribute name="color"> */
		      /* <dia:color val="#000000"/> */
		      /* </dia:attribute> */

		      /* <dia:attribute name="alignment"> */
		      /* <dia:enum val="0"/> */
		      /* </dia:attribute> */

		      fprintf (f, "</dia:composite>\n");
		      fprintf (f, "</dia:attribute>\n");

		      /* <dia:attribute name="valign"> */
		      /* <dia:enum val="3"/> */
		      /* </dia:attribute> */

		      /*
		       * fprintf (f, "<dia:connections>\n");
		       * fprintf (f, "<dia:connection handle=\"0\" to=\"O%d\" connection=\"8\"/>\n", objectcount - 1);
		       * fprintf (f, "</dia:connections>\n");
		       */

		      fprintf (f, "</dia:object>\n");
		    }
		  else
		    {
		      /* dummy node */
		    }
		}
	      dptr = dptr->next;
	    }
	}

      /* now the edges */
      eptr = del;
      while (eptr)
	{
	  if (eptr->style == ESTYLE_INVIS)
	    {
	      /* edge is in layout data but not visible in drawing */
	      eptr = eptr->next;
	      continue;
	    }
	  /* edge to draw */
	  if (eptr->style == ESTYLE_DASHED)
	    {
	    }
	  else if (eptr->style == ESTYLE_DOTTED)
	    {
	    }
	  else
	    {
	      /* solid edge line */
	    }
	  if (eptr->color)
	    {
	    }
	  else
	    {
	      /* black edge line */
	    }
	  /* both nodes of the edge */
	  if (eptr->from && eptr->to)
	    {
	      /* draw normal edge lines and not splines */
	      /* both real nodes */
	      if (eptr->from->un && eptr->to->un)
		{
		  fprintf (f,
			   "<!-- edge from node %s to %s -->\n",
			   eptr->from->un->name, eptr->to->un->name);

		  /*
		   * if (eptr->from->y2 != eptr->from->y3)
		   * from from x2 y2 to from x2 from y3 which is south connect of from node
		   * from from x2 y3 to to x1 y1 which is north connect of to node
		   * else
		   * from from x2 y2 to to x1 y1 which is south to north connect
		   */

		  if (eptr->from->y2 != eptr->from->y3)
		    {
		      fprintf (f,
			       "<dia:object type=\"Standard - Line\" version=\"0\" id=\"O%d\">\n",
			       objectcount);
		      objectcount = objectcount + 1;

		      fprintf (f, "<dia:attribute name=\"obj_pos\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y2 / diafactor);	/* start */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"obj_bb\">\n");
		      fprintf (f, "<dia:rectangle val=\"%f,%f;%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y2 / diafactor, eptr->from->x2 / diafactor, eptr->from->y3 / diafactor);	/* start end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f,
			       "<dia:attribute name=\"conn_endpoints\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y2 / diafactor);	/* start */
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y3 / diafactor);	/* end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"numcp\">\n");
		      fprintf (f, "<dia:int val=\"1\"/>\n");
		      fprintf (f, "</dia:attribute>\n");

		      if (eptr->color != 0)
			{
			  red = (eptr->color & 0x00ff0000) >> 16;
			  green = (eptr->color & 0x0000ff00) >> 8;
			  blue = (eptr->color & 0x000000ff);
			  fprintf (f,
				   "<dia:attribute name=\"line_color\">\n");
			  fprintf (f, "<dia:color val=\"#%02x%02x%02x\"/>\n",
				   red, green, blue);
			  fprintf (f, "</dia:attribute>\n");
			}

		      if (eptr->style)
			{
			  fprintf (f,
				   "<dia:attribute name=\"line_style\">\n");
			  if (eptr->style == ESTYLE_DASHED)
			    {
			      fprintf (f, "<dia:enum val=\"1\"/>\n");
			    }
			  if (eptr->style == ESTYLE_DOTTED)
			    {
			      fprintf (f, "<dia:enum val=\"4\"/>\n");
			    }
			  fprintf (f, "</dia:attribute>\n");
			}

		      fprintf (f, "<dia:connections>\n");
		      fprintf (f,
			       "<dia:connection handle=\"0\" to=\"O%d\" connection=\"6\"/>\n",
			       eptr->from->un->objectnr);
		      fprintf (f, "</dia:connections>\n");

		      fprintf (f, "</dia:object>\n");

		      /* */
		      fprintf (f,
			       "<dia:object type=\"Standard - Line\" version=\"0\" id=\"O%d\">\n",
			       objectcount);
		      objectcount = objectcount + 1;

		      fprintf (f, "<dia:attribute name=\"obj_pos\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y3 / diafactor);	/* start */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"obj_bb\">\n");
		      fprintf (f, "<dia:rectangle val=\"%f,%f;%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y3 / diafactor, eptr->to->x1 / diafactor, eptr->to->y1 / diafactor);	/* start end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f,
			       "<dia:attribute name=\"conn_endpoints\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y3 / diafactor);	/* start */
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->to->x1 / diafactor, eptr->to->y1 / diafactor);	/* end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"numcp\">\n");
		      fprintf (f, "<dia:int val=\"1\"/>\n");
		      fprintf (f, "</dia:attribute>\n");

		      if (eptr->color != 0)
			{
			  red = (eptr->color & 0x00ff0000) >> 16;
			  green = (eptr->color & 0x0000ff00) >> 8;
			  blue = (eptr->color & 0x000000ff);
			  fprintf (f,
				   "<dia:attribute name=\"line_color\">\n");
			  fprintf (f, "<dia:color val=\"#%02x%02x%02x\"/>\n",
				   red, green, blue);
			  fprintf (f, "</dia:attribute>\n");
			}

		      if (eptr->style)
			{
			  fprintf (f,
				   "<dia:attribute name=\"line_style\">\n");
			  if (eptr->style == ESTYLE_DASHED)
			    {
			      fprintf (f, "<dia:enum val=\"1\"/>\n");
			    }
			  if (eptr->style == ESTYLE_DOTTED)
			    {
			      fprintf (f, "<dia:enum val=\"4\"/>\n");
			    }
			  fprintf (f, "</dia:attribute>\n");
			}

		      fprintf (f, "<dia:connections>\n");
		      fprintf (f,
			       "<dia:connection handle=\"1\" to=\"O%d\" connection=\"1\"/>\n",
			       eptr->to->un->objectnr);
		      fprintf (f, "</dia:connections>\n");

		      fprintf (f, "</dia:object>\n");

		    }
		  else
		    {

		      fprintf (f,
			       "<dia:object type=\"Standard - Line\" version=\"0\" id=\"O%d\">\n",
			       objectcount);
		      objectcount = objectcount + 1;

		      fprintf (f, "<dia:attribute name=\"obj_pos\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y2 / diafactor);	/* start */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"obj_bb\">\n");
		      fprintf (f, "<dia:rectangle val=\"%f,%f;%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y2 / diafactor, eptr->to->x1 / diafactor, eptr->to->y1 / diafactor);	/* start end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f,
			       "<dia:attribute name=\"conn_endpoints\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y2 / diafactor);	/* start */
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->to->x1 / diafactor, eptr->to->y1 / diafactor);	/* end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"numcp\">\n");
		      fprintf (f, "<dia:int val=\"1\"/>\n");
		      fprintf (f, "</dia:attribute>\n");

		      if (eptr->color != 0)
			{
			  red = (eptr->color & 0x00ff0000) >> 16;
			  green = (eptr->color & 0x0000ff00) >> 8;
			  blue = (eptr->color & 0x000000ff);
			  fprintf (f,
				   "<dia:attribute name=\"line_color\">\n");
			  fprintf (f, "<dia:color val=\"#%02x%02x%02x\"/>\n",
				   red, green, blue);
			  fprintf (f, "</dia:attribute>\n");
			}

		      if (eptr->style)
			{
			  fprintf (f,
				   "<dia:attribute name=\"line_style\">\n");
			  if (eptr->style == ESTYLE_DASHED)
			    {
			      fprintf (f, "<dia:enum val=\"1\"/>\n");
			    }
			  if (eptr->style == ESTYLE_DOTTED)
			    {
			      fprintf (f, "<dia:enum val=\"4\"/>\n");
			    }
			  fprintf (f, "</dia:attribute>\n");
			}

		      fprintf (f, "<dia:connections>\n");
		      fprintf (f,
			       "<dia:connection handle=\"0\" to=\"O%d\" connection=\"6\"/>\n",
			       eptr->from->un->objectnr);
		      fprintf (f,
			       "<dia:connection handle=\"1\" to=\"O%d\" connection=\"1\"/>\n",
			       eptr->to->un->objectnr);
		      fprintf (f, "</dia:connections>\n");

		      fprintf (f, "</dia:object>\n");

		    }

		}
	      else
		{
		  /* real node to dummynode */
		  if (eptr->from->un && eptr->to->un == NULL)
		    {
		      fprintf (f,
			       "<!-- edge from node %s to %s -->\n",
			       eptr->from->un->name, "dummynode");

		      /*
		       * from from x2 y2 to to x1 y1 at south connection of real node
		       * from to x1 y1 to to x2 y2
		       */

		      fprintf (f,
			       "<dia:object type=\"Standard - Line\" version=\"0\" id=\"O%d\">\n",
			       objectcount);
		      objectcount = objectcount + 1;

		      fprintf (f, "<dia:attribute name=\"obj_pos\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y2 / diafactor);	/* start */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"obj_bb\">\n");
		      fprintf (f, "<dia:rectangle val=\"%f,%f;%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y2 / diafactor, eptr->to->x1 / diafactor, eptr->to->y1 / diafactor);	/* start end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f,
			       "<dia:attribute name=\"conn_endpoints\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y2 / diafactor);	/* start */
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->to->x1 / diafactor, eptr->to->y1 / diafactor);	/* end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"numcp\">\n");
		      fprintf (f, "<dia:int val=\"1\"/>\n");
		      fprintf (f, "</dia:attribute>\n");

		      if (eptr->color != 0)
			{
			  red = (eptr->color & 0x00ff0000) >> 16;
			  green = (eptr->color & 0x0000ff00) >> 8;
			  blue = (eptr->color & 0x000000ff);
			  fprintf (f,
				   "<dia:attribute name=\"line_color\">\n");
			  fprintf (f, "<dia:color val=\"#%02x%02x%02x\"/>\n",
				   red, green, blue);
			  fprintf (f, "</dia:attribute>\n");
			}

		      if (eptr->style)
			{
			  fprintf (f,
				   "<dia:attribute name=\"line_style\">\n");
			  if (eptr->style == ESTYLE_DASHED)
			    {
			      fprintf (f, "<dia:enum val=\"1\"/>\n");
			    }
			  if (eptr->style == ESTYLE_DOTTED)
			    {
			      fprintf (f, "<dia:enum val=\"4\"/>\n");
			    }
			  fprintf (f, "</dia:attribute>\n");
			}

		      fprintf (f, "<dia:connections>\n");
		      fprintf (f,
			       "<dia:connection handle=\"0\" to=\"O%d\" connection=\"6\"/>\n",
			       eptr->from->un->objectnr);
		      fprintf (f, "</dia:connections>\n");

		      fprintf (f, "</dia:object>\n");

		      /* */
		      fprintf (f,
			       "<dia:object type=\"Standard - Line\" version=\"0\" id=\"O%d\">\n",
			       objectcount);
		      objectcount = objectcount + 1;

		      fprintf (f, "<dia:attribute name=\"obj_pos\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->to->x1 / diafactor, eptr->to->y1 / diafactor);	/* start */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"obj_bb\">\n");
		      fprintf (f, "<dia:rectangle val=\"%f,%f;%f,%f\"/>\n", eptr->to->x1 / diafactor, eptr->to->y1 / diafactor, eptr->to->x2 / diafactor, eptr->to->y2 / diafactor);	/* start end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f,
			       "<dia:attribute name=\"conn_endpoints\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->to->x1 / diafactor, eptr->to->y1 / diafactor);	/* start */
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->to->x2 / diafactor, eptr->to->y2 / diafactor);	/* end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"numcp\">\n");
		      fprintf (f, "<dia:int val=\"1\"/>\n");
		      fprintf (f, "</dia:attribute>\n");

		      if (eptr->color != 0)
			{
			  red = (eptr->color & 0x00ff0000) >> 16;
			  green = (eptr->color & 0x0000ff00) >> 8;
			  blue = (eptr->color & 0x000000ff);
			  fprintf (f,
				   "<dia:attribute name=\"line_color\">\n");
			  fprintf (f, "<dia:color val=\"#%02x%02x%02x\"/>\n",
				   red, green, blue);
			  fprintf (f, "</dia:attribute>\n");
			}

		      if (eptr->style)
			{
			  fprintf (f,
				   "<dia:attribute name=\"line_style\">\n");
			  if (eptr->style == ESTYLE_DASHED)
			    {
			      fprintf (f, "<dia:enum val=\"1\"/>\n");
			    }
			  if (eptr->style == ESTYLE_DOTTED)
			    {
			      fprintf (f, "<dia:enum val=\"4\"/>\n");
			    }
			  fprintf (f, "</dia:attribute>\n");
			}

		      fprintf (f, "</dia:object>\n");

		    }
		  else if (eptr->from->un == NULL && eptr->to->un)
		    {
		      /* dummynode to real node */
		      fprintf (f,
			       "<!-- edge from node %s to %s -->\n",
			       "dummynode", eptr->to->un->name);

		      /*
		       * from from x1 y1 to from x2 y2
		       * from from x2 y2 to to x1 y1 which is north of real node
		       */

		      fprintf (f,
			       "<dia:object type=\"Standard - Line\" version=\"0\" id=\"O%d\">\n",
			       objectcount);
		      objectcount = objectcount + 1;

		      fprintf (f, "<dia:attribute name=\"obj_pos\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x1 / diafactor, eptr->from->y1 / diafactor);	/* start */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"obj_bb\">\n");
		      fprintf (f, "<dia:rectangle val=\"%f,%f;%f,%f\"/>\n", eptr->from->x1 / diafactor, eptr->from->y1 / diafactor, eptr->from->x2 / diafactor, eptr->from->y2 / diafactor);	/* start end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f,
			       "<dia:attribute name=\"conn_endpoints\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x1 / diafactor, eptr->from->y1 / diafactor);	/* start */
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y2 / diafactor);	/* end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"numcp\">\n");
		      fprintf (f, "<dia:int val=\"1\"/>\n");
		      fprintf (f, "</dia:attribute>\n");

		      if (eptr->color != 0)
			{
			  red = (eptr->color & 0x00ff0000) >> 16;
			  green = (eptr->color & 0x0000ff00) >> 8;
			  blue = (eptr->color & 0x000000ff);
			  fprintf (f,
				   "<dia:attribute name=\"line_color\">\n");
			  fprintf (f, "<dia:color val=\"#%02x%02x%02x\"/>\n",
				   red, green, blue);
			  fprintf (f, "</dia:attribute>\n");
			}

		      if (eptr->style)
			{
			  fprintf (f,
				   "<dia:attribute name=\"line_style\">\n");
			  if (eptr->style == ESTYLE_DASHED)
			    {
			      fprintf (f, "<dia:enum val=\"1\"/>\n");
			    }
			  if (eptr->style == ESTYLE_DOTTED)
			    {
			      fprintf (f, "<dia:enum val=\"4\"/>\n");
			    }
			  fprintf (f, "</dia:attribute>\n");
			}

		      fprintf (f, "</dia:object>\n");

		      /* */
		      fprintf (f,
			       "<dia:object type=\"Standard - Line\" version=\"0\" id=\"O%d\">\n",
			       objectcount);
		      objectcount = objectcount + 1;

		      fprintf (f, "<dia:attribute name=\"obj_pos\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y2 / diafactor);	/* start */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"obj_bb\">\n");
		      fprintf (f, "<dia:rectangle val=\"%f,%f;%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y2 / diafactor, eptr->to->x1 / diafactor, eptr->to->y1 / diafactor);	/* start end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f,
			       "<dia:attribute name=\"conn_endpoints\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y2 / diafactor);	/* start */
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->to->x1 / diafactor, eptr->to->y1 / diafactor);	/* end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"numcp\">\n");
		      fprintf (f, "<dia:int val=\"1\"/>\n");
		      fprintf (f, "</dia:attribute>\n");

		      if (eptr->color != 0)
			{
			  red = (eptr->color & 0x00ff0000) >> 16;
			  green = (eptr->color & 0x0000ff00) >> 8;
			  blue = (eptr->color & 0x000000ff);
			  fprintf (f,
				   "<dia:attribute name=\"line_color\">\n");
			  fprintf (f, "<dia:color val=\"#%02x%02x%02x\"/>\n",
				   red, green, blue);
			  fprintf (f, "</dia:attribute>\n");
			}

		      if (eptr->style)
			{
			  fprintf (f,
				   "<dia:attribute name=\"line_style\">\n");
			  if (eptr->style == ESTYLE_DASHED)
			    {
			      fprintf (f, "<dia:enum val=\"1\"/>\n");
			    }
			  if (eptr->style == ESTYLE_DOTTED)
			    {
			      fprintf (f, "<dia:enum val=\"4\"/>\n");
			    }
			  fprintf (f, "</dia:attribute>\n");
			}

		      fprintf (f, "<dia:connections>\n");
		      fprintf (f,
			       "<dia:connection handle=\"0\" to=\"O%d\" connection=\"1\"/>\n",
			       eptr->to->un->objectnr);
		      fprintf (f, "</dia:connections>\n");

		      fprintf (f, "</dia:object>\n");

		    }
		  else
		    {
		      /* both dummynode */
		      fprintf (f,
			       "<!-- edge from node %s to %s -->\n",
			       "dummynode", "dummynode");

		      /* from from x1 y1 to from x2 y2
		       * from from x2 y2 to to x1 y1
		       * from to x1 y1 to to x2 y2
		       */

		      fprintf (f,
			       "<dia:object type=\"Standard - Line\" version=\"0\" id=\"O%d\">\n",
			       objectcount);
		      objectcount = objectcount + 1;

		      fprintf (f, "<dia:attribute name=\"obj_pos\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x1 / diafactor, eptr->from->y1 / diafactor);	/* start */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"obj_bb\">\n");
		      fprintf (f, "<dia:rectangle val=\"%f,%f;%f,%f\"/>\n", eptr->from->x1 / diafactor, eptr->from->y1 / diafactor, eptr->from->x2 / diafactor, eptr->from->y2 / diafactor);	/* start end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f,
			       "<dia:attribute name=\"conn_endpoints\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x1 / diafactor, eptr->from->y1 / diafactor);	/* start */
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y2 / diafactor);	/* end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"numcp\">\n");
		      fprintf (f, "<dia:int val=\"1\"/>\n");
		      fprintf (f, "</dia:attribute>\n");

		      if (eptr->color != 0)
			{
			  red = (eptr->color & 0x00ff0000) >> 16;
			  green = (eptr->color & 0x0000ff00) >> 8;
			  blue = (eptr->color & 0x000000ff);
			  fprintf (f,
				   "<dia:attribute name=\"line_color\">\n");
			  fprintf (f, "<dia:color val=\"#%02x%02x%02x\"/>\n",
				   red, green, blue);
			  fprintf (f, "</dia:attribute>\n");
			}

		      if (eptr->style)
			{
			  fprintf (f,
				   "<dia:attribute name=\"line_style\">\n");
			  if (eptr->style == ESTYLE_DASHED)
			    {
			      fprintf (f, "<dia:enum val=\"1\"/>\n");
			    }
			  if (eptr->style == ESTYLE_DOTTED)
			    {
			      fprintf (f, "<dia:enum val=\"4\"/>\n");
			    }
			  fprintf (f, "</dia:attribute>\n");
			}

		      fprintf (f, "</dia:object>\n");

		      /* */
		      fprintf (f,
			       "<dia:object type=\"Standard - Line\" version=\"0\" id=\"O%d\">\n",
			       objectcount);
		      objectcount = objectcount + 1;

		      fprintf (f, "<dia:attribute name=\"obj_pos\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y2 / diafactor);	/* start */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"obj_bb\">\n");
		      fprintf (f, "<dia:rectangle val=\"%f,%f;%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y2 / diafactor, eptr->to->x1 / diafactor, eptr->to->y1 / diafactor);	/* start end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f,
			       "<dia:attribute name=\"conn_endpoints\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->from->x2 / diafactor, eptr->from->y2 / diafactor);	/* start */
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->to->x1 / diafactor, eptr->to->y1 / diafactor);	/* end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"numcp\">\n");
		      fprintf (f, "<dia:int val=\"1\"/>\n");
		      fprintf (f, "</dia:attribute>\n");

		      if (eptr->color != 0)
			{
			  red = (eptr->color & 0x00ff0000) >> 16;
			  green = (eptr->color & 0x0000ff00) >> 8;
			  blue = (eptr->color & 0x000000ff);
			  fprintf (f,
				   "<dia:attribute name=\"line_color\">\n");
			  fprintf (f, "<dia:color val=\"#%02x%02x%02x\"/>\n",
				   red, green, blue);
			  fprintf (f, "</dia:attribute>\n");
			}

		      if (eptr->style)
			{
			  fprintf (f,
				   "<dia:attribute name=\"line_style\">\n");
			  if (eptr->style == ESTYLE_DASHED)
			    {
			      fprintf (f, "<dia:enum val=\"1\"/>\n");
			    }
			  if (eptr->style == ESTYLE_DOTTED)
			    {
			      fprintf (f, "<dia:enum val=\"4\"/>\n");
			    }
			  fprintf (f, "</dia:attribute>\n");
			}

		      fprintf (f, "</dia:object>\n");

		      /* */
		      fprintf (f,
			       "<dia:object type=\"Standard - Line\" version=\"0\" id=\"O%d\">\n",
			       objectcount);
		      objectcount = objectcount + 1;

		      fprintf (f, "<dia:attribute name=\"obj_pos\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->to->x1 / diafactor, eptr->to->y1 / diafactor);	/* start */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"obj_bb\">\n");
		      fprintf (f, "<dia:rectangle val=\"%f,%f;%f,%f\"/>\n", eptr->to->x1 / diafactor, eptr->to->y1 / diafactor, eptr->to->x2 / diafactor, eptr->to->y2 / diafactor);	/* start end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f,
			       "<dia:attribute name=\"conn_endpoints\">\n");
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->to->x1 / diafactor, eptr->to->y1 / diafactor);	/* start */
		      fprintf (f, "<dia:point val=\"%f,%f\"/>\n", eptr->to->x2 / diafactor, eptr->to->y2 / diafactor);	/* end */
		      fprintf (f, "</dia:attribute>\n");

		      fprintf (f, "<dia:attribute name=\"numcp\">\n");
		      fprintf (f, "<dia:int val=\"1\"/>\n");
		      fprintf (f, "</dia:attribute>\n");

		      if (eptr->color != 0)
			{
			  red = (eptr->color & 0x00ff0000) >> 16;
			  green = (eptr->color & 0x0000ff00) >> 8;
			  blue = (eptr->color & 0x000000ff);
			  fprintf (f,
				   "<dia:attribute name=\"line_color\">\n");
			  fprintf (f, "<dia:color val=\"#%02x%02x%02x\"/>\n",
				   red, green, blue);
			  fprintf (f, "</dia:attribute>\n");
			}

		      if (eptr->style)
			{
			  fprintf (f,
				   "<dia:attribute name=\"line_style\">\n");
			  if (eptr->style == ESTYLE_DASHED)
			    {
			      fprintf (f, "<dia:enum val=\"1\"/>\n");
			    }
			  if (eptr->style == ESTYLE_DOTTED)
			    {
			      fprintf (f, "<dia:enum val=\"4\"/>\n");
			    }
			  fprintf (f, "</dia:attribute>\n");
			}

		      fprintf (f, "</dia:object>\n");

		    }
		}
	    }
	  eptr = eptr->next;
	}

      /* tail */
      fprintf (f, "</dia:layer>\n");
      fprintf (f, "</dia:diagram>\n");
      /* ready */
      fclose (f);
    }

  mgr_free (diafilename);
  return;
}

/* treeview */
static void on_changed (GtkWidget * widget, gpointer statusbar);
static GtkTreeModel *create_and_fill_model (void);
static void create_view_and_model (GtkWidget * view);

/* treeview changed */
static void
on_changed (GtkWidget * widget, gpointer statusbar)
{
  GtkTreeIter iter;
  GtkTreeModel *model;
  char *value;
  struct usubg *subg = NULL;
  struct unode *un = NULL;
  struct uedge *ue = NULL;
  GtkTextBuffer *buffer;
  const char *text = NULL;
  const char *label = NULL;
  char buf[1024];

  /* */
  if (gtk_tree_selection_get_selected
      (GTK_TREE_SELECTION (widget), &model, &iter))
    {
      /* get string of selected node in value */
      gtk_tree_model_get (model, &iter, 0, &value, 1, &subg, 2, &un, 3, &ue,
			  -1);

      text = "";

      /* Obtaining the buffer associated with the widget. */
      buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (treeviewtext_view));

      if (subg == NULL && un == NULL && ue == NULL)
	{
	  /* Set the default buffer text. */
	  gtk_text_buffer_set_text (buffer, "", -1);
	}
      else
	{
	  memset (buf, 0, 1024);
	  if (subg)
	    {
	      snprintf (buf, 1024 - 1, "subgraph %s\nlabel %s\n", subg->name,
			subg->label);
	    }
	  else if (un)
	    {
	      if (un->label)
		{
		  label = un->label;
		}
	      else
		{
		  label = "[dummynode]";
		}
	      snprintf (buf, 1024 - 1, "node %s\nlabel %s\n", un->name,
			label);
	    }
	  else if (ue)
	    {
	      if (ue->label)
		{
		  label = ue->label;
		}
	      else
		{
		  label = "\"\"";
		}
	      snprintf (buf, 1024 - 1, "edge %s->%s\nedgelabel %s\n",
			ue->fn->name, ue->tn->name, label);
	    }
	  else
	    {
	      strcpy (buf, "shouldnothappen");
	    }
	  text = uniqstring (buf);
	  /* Set the default buffer text. */
	  gtk_text_buffer_set_text (buffer, text, -1);
	}

      /* put the string in the statusbar */
      gtk_statusbar_push (GTK_STATUSBAR (statusbar),
			  gtk_statusbar_get_context_id (GTK_STATUSBAR
							(statusbar), value),
			  value);
      g_free (value);
    }

  return;
}

/* */
static void
create_and_fill_model_rootnodes (GtkTreeStore * treestore,
				 GtkTreeIter toplevel)
{
  struct dln *nlp = NULL;
  char buf[16];
  char *text = NULL;
  GtkTreeIter child;

  nlp = nl;

  while (nlp)
    {
      if (nlp->un->rootedon == NULL)
	{
	  memset (buf, 0, 16);
	  snprintf (buf, 16 - 1, "%s", nlp->un->name);
	  text = uniqstring (buf);
	  gtk_tree_store_append (treestore, &child, &toplevel);
	  gtk_tree_store_set (treestore, &child, 0, text, 1, NULL, 2, nlp->un,
			      3, NULL, -1);
	}
      nlp = nlp->next;
    }

  return;
}


static void
create_and_fill_model_rootedges (GtkTreeStore * treestore,
				 GtkTreeIter toplevel)
{
  struct dle *elp = NULL;
  char buff[16];
  char buft[16];
  char buf[35];
  char *text = NULL;
  GtkTreeIter child;

  elp = el;

  while (elp)
    {
      if (elp->ue->rootedon == NULL)
	{
	  memset (buff, 0, 16);
	  memset (buft, 0, 16);
	  memset (buf, 0, 35);
	  snprintf (buff, 16 - 1, "%s", elp->ue->fn->name);
	  snprintf (buft, 16 - 1, "%s", elp->ue->tn->name);
	  strcpy (buf, buff);
	  strcat (buf, "->");
	  strcat (buf, buft);
	  text = uniqstring (buf);
	  gtk_tree_store_append (treestore, &child, &toplevel);
	  gtk_tree_store_set (treestore, &child, 0, text, 1, NULL, 2, NULL,
			      3, elp->ue, -1);
	}
      elp = elp->next;
    }

  return;
}


static void
create_and_fill_model_rootsubg (GtkTreeStore * treestore,
				GtkTreeIter toplevel, struct usubg *subg)
{
  char buff[16];
  char buft[16];
  char buf[35];
  char *text = NULL;
  struct dlsg *sgp = NULL;
  struct dln *nlp = NULL;
  struct dle *elp = NULL;
  GtkTreeIter child;
  GtkTreeIter child2;
  memset (buf, 0, 20);
  snprintf (buf, 20 - 1, "sub %s", subg->name);
  text = uniqstring (buf);
  gtk_tree_store_append (treestore, &child, &toplevel);
  gtk_tree_store_set (treestore, &child, 0, text, 1, subg, 2, NULL,
		      3, NULL, -1);

  /* nodes in this subgraph */
  if (subg->nl)
    {
      nlp = subg->nl;
      while (nlp)
	{
	  memset (buf, 0, 35);
	  snprintf (buf, 16 - 1, "%s", nlp->un->name);
	  text = uniqstring (buf);
	  gtk_tree_store_append (treestore, &child2, &child);
	  gtk_tree_store_set (treestore, &child2, 0, text, 1, NULL, 2,
			      nlp->un, 3, NULL, -1);
	  nlp = nlp->next;
	}
    }

  /* edges in this subgraph */
  if (subg->el)
    {
      elp = subg->el;
      while (elp)
	{
	  memset (buff, 0, 16);
	  memset (buft, 0, 16);
	  memset (buf, 0, 35);
	  snprintf (buff, 16 - 1, "%s", elp->ue->fn->name);
	  snprintf (buft, 16 - 1, "%s", elp->ue->tn->name);
	  strcpy (buf, buff);
	  strcat (buf, "->");
	  strcat (buf, buft);
	  text = uniqstring (buf);
	  gtk_tree_store_append (treestore, &child2, &child);
	  gtk_tree_store_set (treestore, &child2, 0, text, 1, NULL, 2, NULL,
			      3, elp->ue, -1);
	  elp = elp->next;
	}
    }

  /* subgraphs of this subgraph */
  if (subg->sg)
    {
      sgp = subg->sg;
      while (sgp)
	{
	  create_and_fill_model_rootsubg (treestore, child, sgp->sg);
	  sgp = sgp->next;
	}
    }

  return;
}

/* */
static GtkTreeModel *
create_and_fill_model (void)
{
  GtkTreeStore *treestore;
  GtkTreeIter toplevel;
  struct usubg *sgp = NULL;

  treestore =
    gtk_tree_store_new (4, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER,
			G_TYPE_POINTER);

  if (nl)
    {
      gtk_tree_store_append (treestore, &toplevel, NULL);
      gtk_tree_store_set (treestore, &toplevel, 0, "Nodes", 1, NULL, 2, NULL,
			  3, NULL, -1);
      create_and_fill_model_rootnodes (treestore, toplevel);
    }

  if (el)
    {
      gtk_tree_store_append (treestore, &toplevel, NULL);
      gtk_tree_store_set (treestore, &toplevel, 0, "Edges", 1, NULL, 2, NULL,
			  3, NULL, -1);
      create_and_fill_model_rootedges (treestore, toplevel);
    }

  if (sgl)
    {
      gtk_tree_store_append (treestore, &toplevel, NULL);
      gtk_tree_store_set (treestore, &toplevel, 0, "Subgraphs", 1, NULL, 2,
			  NULL, 3, NULL, -1);
      sgp = sgl;
      while (sgp)
	{
	  if (sgp->rootedon == NULL)
	    {
	      create_and_fill_model_rootsubg (treestore, toplevel, sgp);
	    }
	  sgp = sgp->next;
	}
    }

  return GTK_TREE_MODEL (treestore);
}

/* */
static void
create_view_and_model (GtkWidget * view)
{
  GtkTreeViewColumn *col;
  GtkCellRenderer *renderer;
  GtkTreeModel *model;
  const char *title = NULL;

  if (inputfilename)
    {
      title = basename (inputfilename);
      title = uniqstring ((char *) title);
    }
  else
    {
      title = "filename";
    }

  col = gtk_tree_view_column_new ();
  gtk_tree_view_column_set_title (col, title);
  gtk_tree_view_append_column (GTK_TREE_VIEW (view), col);

  renderer = gtk_cell_renderer_text_new ();
  gtk_tree_view_column_pack_start (col, renderer, TRUE);
  gtk_tree_view_column_add_attribute (col, renderer, "text", 0);

  model = create_and_fill_model ();
  gtk_tree_view_set_model (GTK_TREE_VIEW (view), model);
  g_object_unref (model);

  return;
}

/* */
static void
on_treeview_window_destroy (GtkWidget * widget, gpointer data)
{
  if (widget)
    {
    }
  if (data)
    {
    }
  if (treeview_window)
    {
      treeview_window = NULL;
    }
  return;
}

static void
dotreeview (void)
{
  GtkWidget *view;
  GtkTreeSelection *selection;
  GtkWidget *vbox;
  GtkWidget *hbox;
  GtkWidget *statusbar;
  GtkWidget *scrollview;

  GtkWidget *treeviewtext_scroll;

  treeview_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position (GTK_WINDOW (treeview_window), GTK_WIN_POS_CENTER);
  gtk_window_set_title (GTK_WINDOW (treeview_window), (char *) "Treeview");
  gtk_widget_set_size_request (treeview_window, 450, 300);

  vbox = gtk_vbox_new (FALSE, 2);
  gtk_container_add (GTK_CONTAINER (treeview_window), vbox);

  /* add next area to the vbox1 */
  hbox = gtk_hbox_new (FALSE, 0);

  gtk_box_pack_start ( /* box */ GTK_BOX (vbox), /* child */ hbox,
		      /* expand */ TRUE, /* fill */ TRUE,	/* padding */
		      PACKPADDING);
  gtk_widget_show (hbox);

  /* create treeview itself */
  view = gtk_tree_view_new ();

  /* add the data to the treeview model */
  create_view_and_model (view);
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));

  /* add scrollbars to treeview */
  scrollview = gtk_scrolled_window_new (NULL, NULL);
  gtk_container_add (GTK_CONTAINER (scrollview), view);

  gtk_box_pack_start (GTK_BOX (hbox), scrollview, TRUE, TRUE, 1);

  /* Create a multiline text widget. */
  treeviewtext_view = gtk_text_view_new ();

  /* help text cannot be edited in the window */
  gtk_text_view_set_editable (GTK_TEXT_VIEW (treeviewtext_view), FALSE);

  /* wrap by word */
  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (treeviewtext_view),
			       GTK_WRAP_WORD);

  /* add scroll to the text area */
  treeviewtext_scroll = gtk_scrolled_window_new (NULL, NULL);
  gtk_container_add (GTK_CONTAINER (treeviewtext_scroll), treeviewtext_view);

  gtk_box_pack_start (GTK_BOX (hbox), treeviewtext_scroll, TRUE, TRUE, 1);

  /* selected item status line */
  statusbar = gtk_statusbar_new ();
  gtk_box_pack_start (GTK_BOX (vbox), statusbar, FALSE, TRUE, 1);

  g_signal_connect (selection, "changed", G_CALLBACK (on_changed), statusbar);

  g_signal_connect (G_OBJECT (treeview_window), "destroy",
		    G_CALLBACK (on_treeview_window_destroy), NULL);

  gtk_widget_show_all (treeview_window);

  return;
}

/* End. */
